am 7a61b840: am 8f36a744: am cd24dae9: Merge "Fix bug 2605504 Don\'t leak a metric ton of NotificationPlayer threads The Looper on the thread created in order to be notified of the playback completion of notification sounds, was never stopped, causing the threads to s
diff --git a/Android.mk b/Android.mk
index 8783934..f6aa1e9c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -197,6 +197,9 @@
 LOCAL_MODULE := framework
 LOCAL_MODULE_CLASS := JAVA_LIBRARIES
 
+LOCAL_NO_EMMA_INSTRUMENT := true
+LOCAL_NO_EMMA_COMPILE := true
+
 # List of classes and interfaces which should be loaded by the Zygote.
 LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/preloaded-classes
 
@@ -583,6 +586,9 @@
 
 LOCAL_MODULE := ext
 
+LOCAL_NO_EMMA_INSTRUMENT := true
+LOCAL_NO_EMMA_COMPILE := true
+
 include $(BUILD_JAVA_LIBRARY)
 
 
diff --git a/api/current.xml b/api/current.xml
index 721d133..ac39248 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -2132,6 +2132,17 @@
  visibility="public"
 >
 </field>
+<field name="adapter"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843521"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="addStatesFromChildren"
  type="int"
  transient="false"
@@ -2341,6 +2352,17 @@
  visibility="public"
 >
 </field>
+<field name="as"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843527"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="author"
  type="int"
  transient="false"
@@ -3001,6 +3023,17 @@
  visibility="public"
 >
 </field>
+<field name="column"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843530"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="columnDelay"
  type="int"
  transient="false"
@@ -4167,6 +4200,17 @@
  visibility="public"
 >
 </field>
+<field name="from"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843525"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="fromAlpha"
  type="int"
  transient="false"
@@ -4189,6 +4233,17 @@
  visibility="public"
 >
 </field>
+<field name="fromValue"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843528"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="fromXDelta"
  type="int"
  transient="false"
@@ -8314,6 +8369,17 @@
  visibility="public"
 >
 </field>
+<field name="selection"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843522"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="settingsActivity"
  type="int"
  transient="false"
@@ -8501,6 +8567,17 @@
  visibility="public"
 >
 </field>
+<field name="sortOrder"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843523"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="soundEffectsEnabled"
  type="int"
  transient="false"
@@ -9689,6 +9766,17 @@
  visibility="public"
 >
 </field>
+<field name="to"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843526"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="toAlpha"
  type="int"
  transient="false"
@@ -9711,6 +9799,17 @@
  visibility="public"
 >
 </field>
+<field name="toValue"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843529"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="toXDelta"
  type="int"
  transient="false"
@@ -9887,6 +9986,17 @@
  visibility="public"
 >
 </field>
+<field name="uri"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843524"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="useLevel"
  type="int"
  transient="false"
@@ -10437,6 +10547,28 @@
  visibility="public"
 >
 </field>
+<field name="withClass"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843532"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="withExpression"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843531"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="writePermission"
  type="int"
  transient="false"
@@ -20304,6 +20436,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="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="openOptionsMenu"
  return="void"
  abstract="false"
@@ -24658,6 +24801,300 @@
 </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>
+<constructor name="Fragment"
+ type="android.app.Fragment"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="Fragment"
+ type="android.app.Fragment"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</constructor>
+<method name="getActivity"
+ return="android.app.Activity"
+ 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="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="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="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>
+</method>
+<method name="onDestroy"
+ 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="onLowMemory"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onPause"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onRestart"
+ 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="savedInstanceState" type="android.os.Bundle">
+</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="onRetainNonConfigurationInstance"
+ return="java.lang.Object"
+ 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>
+</class>
+<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="containerViewId" type="int">
+</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="fragment" type="android.app.Fragment">
+</parameter>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="containerViewId" type="int">
+</parameter>
+</method>
+<method name="commit"
+ return="void"
+ 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>
+</interface>
 <class name="Instrumentation"
  extends="java.lang.Object"
  abstract="false"
@@ -38074,6 +38511,17 @@
  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"
@@ -44699,6 +45147,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"
@@ -44830,6 +45293,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"
@@ -55631,6 +56109,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"
@@ -56515,6 +57014,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"
@@ -57383,6 +57914,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"
@@ -57504,6 +58046,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"
@@ -57621,6 +58174,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"
@@ -57651,6 +58223,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"
@@ -57870,6 +58459,19 @@
 <parameter name="lockingEnabled" type="boolean">
 </parameter>
 </method>
+<method name="setMaxSqlCacheSize"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cacheSize" type="int">
+</parameter>
+</method>
 <method name="setMaximumSize"
  return="long"
  abstract="false"
@@ -58072,6 +58674,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"
@@ -72930,6 +73543,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"
@@ -73824,6 +74448,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"
@@ -124159,6 +124813,240 @@
 </method>
 </class>
 </package>
+<package name="android.os.storage"
+>
+<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"
+ static="false"
+ final="false"
+ 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="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="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="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"
 >
 <class name="CheckBoxPreference"
@@ -124952,6 +125840,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"
@@ -133322,6 +134352,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"
@@ -133333,6 +134374,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"
@@ -158103,7 +159155,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>
@@ -201218,6 +202270,195 @@
 </parameter>
 </method>
 </interface>
+<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"
@@ -203179,6 +204420,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"
@@ -203326,6 +204581,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"
@@ -203397,6 +204669,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"
@@ -209404,6 +210698,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"
diff --git a/cmds/input/input b/cmds/input/input
old mode 100644
new mode 100755
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a962391..15bf242 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -650,6 +650,65 @@
     private CharSequence mTitle;
     private int mTitleColor = 0;
 
+    final FragmentManager mFragments = new FragmentManager();
+    
+    private final class FragmentTransactionImpl implements FragmentTransaction {
+        ArrayList<Fragment> mAdded;
+        ArrayList<Fragment> mRemoved;
+        
+        public FragmentTransaction add(Fragment fragment, int containerViewId) {
+            return add(fragment, null, containerViewId);
+        }
+
+        public FragmentTransaction add(Fragment fragment, String name, int containerViewId) {
+            if (fragment.mActivity != null) {
+                throw new IllegalStateException("Fragment already added: " + fragment);
+            }
+            if (name != null) {
+                fragment.mName = name;
+            }
+            if (mRemoved != null) {
+                mRemoved.remove(fragment);
+            }
+            if (mAdded == null) {
+                mAdded = new ArrayList<Fragment>();
+            }
+            fragment.mContainerId = containerViewId;
+            mAdded.add(fragment);
+            return this;
+        }
+
+        public FragmentTransaction remove(Fragment fragment) {
+            if (fragment.mActivity == null) {
+                throw new IllegalStateException("Fragment not added: " + fragment);
+            }
+            if (mAdded != null) {
+                mAdded.remove(fragment);
+            }
+            if (mRemoved == null) {
+                mRemoved = new ArrayList<Fragment>();
+            }
+            mRemoved.add(fragment);
+            return this;
+        }
+
+        public void commit() {
+            if (mRemoved != null) {
+                for (int i=mRemoved.size()-1; i>=0; i--) {
+                    mFragments.removeFragment(mRemoved.get(i));
+                }
+            }
+            if (mAdded != null) {
+                for (int i=mAdded.size()-1; i>=0; i--) {
+                    mFragments.addFragment(mAdded.get(i));
+                }
+            }
+            if (mFragments != null) {
+                mFragments.moveToState(mFragments.mCurState);
+            }
+        }
+    }
+    
     private static final class ManagedCursor {
         ManagedCursor(Cursor cursor) {
             mCursor = cursor;
@@ -1464,6 +1523,14 @@
     }
     
     /**
+     * Start a series of edit operations on the Fragments associated with
+     * this activity.
+     */
+    public FragmentTransaction openFragmentTransaction() {
+        return new FragmentTransactionImpl();
+    }
+    
+    /**
      * Wrapper around
      * {@link ContentResolver#query(android.net.Uri , String[], String, String[], String)}
      * that gives the resulting {@link Cursor} to call
@@ -3743,6 +3810,8 @@
             Configuration config) {
         attachBaseContext(context);
 
+        mFragments.attachActivity(this);
+        
         mWindow = PolicyManager.makeNewWindow(this);
         mWindow.setCallback(this);
         if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
@@ -3776,6 +3845,11 @@
         return mParent != null ? mParent.getActivityToken() : mToken;
     }
 
+    final void performCreate(Bundle icicle) {
+        onCreate(icicle);
+        mFragments.dispatchCreate(icicle);
+    }
+    
     final void performStart() {
         mCalled = false;
         mInstrumentation.callActivityOnStart(this);
@@ -3784,6 +3858,7 @@
                 "Activity " + mComponent.toShortString() +
                 " did not call through to super.onStart()");
         }
+        mFragments.dispatchStart();
     }
     
     final void performRestart() {
@@ -3830,6 +3905,9 @@
         // Now really resume, and install the current status bar and menu.
         mResumed = true;
         mCalled = false;
+        
+        mFragments.dispatchResume();
+        
         onPostResume();
         if (!mCalled) {
             throw new SuperNotCalledException(
@@ -3839,6 +3917,7 @@
     }
 
     final void performPause() {
+        mFragments.dispatchPause();
         onPause();
     }
     
@@ -3853,6 +3932,8 @@
                 mWindow.closeAllPanels();
             }
 
+            mFragments.dispatchStop();
+            
             mCalled = false;
             mInstrumentation.callActivityOnStop(this);
             if (!mCalled) {
@@ -3877,6 +3958,11 @@
         mResumed = false;
     }
 
+    final void performDestroy() {
+        mFragments.dispatchDestroy();
+        onDestroy();
+    }
+    
     final boolean isResumed() {
         return mResumed;
     }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c9096cf..b3223e5 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -145,13 +145,6 @@
     public static final int RECENT_WITH_EXCLUDED = 0x0001;
     
     /**
-     * @hide
-     * TODO: Make this public.  Provides a list that does not contain any
-     * recent tasks that currently are not available to the user.
-     */
-    public static final int RECENT_IGNORE_UNAVAILABLE = 0x0002;
-    
-    /**
      * Return a list of the tasks that the user has recently launched, with
      * the most recent being first and older ones after in order.
      * 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index fd84859..87b77d65 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1465,7 +1465,7 @@
         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 DB_INFO_FORMAT = "  %8d %8d %10d  %s";
+        private static final String DB_INFO_FORMAT = "  %4d %6d %8d %14s  %s";
 
         // Formatting for checkin service - update version if row format changes
         private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 1;
@@ -1865,7 +1865,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(',');
                 }
 
@@ -1916,11 +1916,12 @@
             int N = stats.dbStats.size();
             if (N > 0) {
                 pw.println(" DATABASES");
-                printRow(pw, "  %8s %8s %10s  %s", "Pagesize", "Dbsize", "Lookaside", "Dbname");
+                printRow(pw, "  %4s %6s %8s %14s  %s", "pgsz", "dbsz", "lkaside", "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);
+                            dbStats.lookaside, dbStats.cache, dbStats.dbName);
                 }
             }
 
@@ -3642,7 +3643,7 @@
             }
             try {
                 r.activity.mCalled = false;
-                r.activity.onDestroy();
+                mInstrumentation.callActivityOnDestroy(r.activity);
                 if (!r.activity.mCalled) {
                     throw new SuperNotCalledException(
                         "Activity " + safeToComponentShortString(r.intent) +
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 15ce2b2..e646827 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2776,6 +2776,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) {
@@ -2818,6 +2825,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/Fragment.java b/core/java/android/app/Fragment.java
new file mode 100644
index 0000000..c0dc869
--- /dev/null
+++ b/core/java/android/app/Fragment.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.app;
+
+import android.content.ComponentCallbacks;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A Fragment is a piece of an application's user interface or behavior
+ * that can be placed in an {@link Activity}.
+ */
+public class Fragment implements ComponentCallbacks {
+    static final int INITIALIZING = 0;  // Not yet created.
+    static final int CREATED = 1;       // Created.
+    static final int STARTED = 2;       // Created and started, not resumed.
+    static final int RESUMED = 3;       // Created started and resumed.
+    
+    String mName;
+    
+    int mState = INITIALIZING;
+    Activity mActivity;
+    
+    boolean mCalled;
+    int mContainerId;
+    
+    ViewGroup mContainer;
+    View mView;
+    
+    public Fragment() {
+    }
+    
+    public Fragment(String name) {
+        mName = name;
+    }
+    
+    public String getName() {
+        return mName;
+    }
+    
+    public Activity getActivity() {
+        return mActivity;
+    }
+    
+    public void onAttach(Activity activity) {
+        mCalled = true;
+    }
+    
+    public void onCreate(Bundle savedInstanceState) {
+        mCalled = true;
+    }
+    
+    public View onCreateView(LayoutInflater inflater, ViewGroup container) {
+        return null;
+    }
+    
+    public void onRestoreInstanceState(Bundle savedInstanceState) {
+    }
+    
+    public void onStart() {
+        mCalled = true;
+    }
+    
+    public void onRestart() {
+        mCalled = true;
+    }
+    
+    public void onResume() {
+        mCalled = true;
+    }
+    
+    public void onSaveInstanceState(Bundle outState) {
+    }
+    
+    public void onConfigurationChanged(Configuration newConfig) {
+        mCalled = true;
+    }
+    
+    public Object onRetainNonConfigurationInstance() {
+        return null;
+    }
+    
+    public void onPause() {
+        mCalled = true;
+    }
+    
+    public void onStop() {
+        mCalled = true;
+    }
+    
+    public void onLowMemory() {
+        mCalled = true;
+    }
+    
+    public void onDestroy() {
+        mCalled = true;
+    }
+    
+    public void onDetach() {
+        mCalled = true;
+    }
+}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
new file mode 100644
index 0000000..d5e49cf
--- /dev/null
+++ b/core/java/android/app/FragmentManager.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * Container for fragments associated with an activity.
+ */
+class FragmentManager {
+    final ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
+    
+    int mCurState = Fragment.INITIALIZING;
+    Activity mActivity;
+    
+    void moveToState(Fragment f, int newState) {
+        if (f.mState < newState) {
+            switch (f.mState) {
+                case Fragment.INITIALIZING:
+                    f.mActivity = mActivity;
+                    f.mCalled = false;
+                    f.onAttach(mActivity);
+                    if (!f.mCalled) {
+                        throw new SuperNotCalledException("Fragment " + f
+                                + " did not call through to super.onAttach()");
+                    }
+                    f.mCalled = false;
+                    f.onCreate(null);
+                    if (!f.mCalled) {
+                        throw new SuperNotCalledException("Fragment " + f
+                                + " did not call through to super.onCreate()");
+                    }
+                    
+                    ViewGroup container = null;
+                    if (f.mContainerId != 0) {
+                        container = (ViewGroup)mActivity.findViewById(f.mContainerId);
+                        if (container == null) {
+                            throw new IllegalArgumentException("New view found for id 0x"
+                                    + Integer.toHexString(f.mContainerId)
+                                    + " for fragment " + f);
+                        }
+                    }
+                    f.mContainer = container;
+                    f.mView = f.onCreateView(mActivity.getLayoutInflater(), container);
+                    if (container != null && f.mView != null) {
+                        container.addView(f.mView);
+                    }
+                    
+                case Fragment.CREATED:
+                    if (newState > Fragment.CREATED) {
+                        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) {
+                        f.mCalled = false;
+                        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) {
+                        f.mCalled = false;
+                        f.onPause();
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onPause()");
+                        }
+                    }
+                case Fragment.STARTED:
+                    if (newState < Fragment.STARTED) {
+                        f.mCalled = false;
+                        f.onStop();
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onStop()");
+                        }
+                    }
+                case Fragment.CREATED:
+                    if (newState < Fragment.CREATED) {
+                        if (f.mContainer != null && f.mView != null) {
+                            f.mContainer.removeView(f.mView);
+                        }
+                        f.mContainer = null;
+                        f.mView = null;
+                        
+                        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.mActivity = null;
+                    }
+            }
+        }
+        
+        f.mState = newState;
+    }
+    
+    void moveToState(int newState) {
+        if (mActivity == null && newState != Fragment.INITIALIZING) {
+            throw new IllegalStateException("No activity");
+        }
+        
+        mCurState = newState;
+        for (int i=0; i<mFragments.size(); i++) {
+            Fragment f = mFragments.get(i);
+            moveToState(f, newState);
+        }
+    }
+    
+    public void addFragment(Fragment fragment) {
+        mFragments.add(fragment);
+    }
+    
+    public void removeFragment(Fragment fragment) {
+        mFragments.remove(fragment);
+        moveToState(fragment, Fragment.INITIALIZING);
+    }
+    
+    public void attachActivity(Activity activity) {
+        if (mActivity != null) throw new IllegalStateException();
+        mActivity = activity;
+    }
+    
+    public void dispatchCreate(Bundle state) {
+        moveToState(Fragment.CREATED);
+    }
+    
+    public void dispatchStart() {
+        moveToState(Fragment.STARTED);
+    }
+    
+    public void dispatchResume() {
+        moveToState(Fragment.RESUMED);
+    }
+    
+    public void dispatchPause() {
+        moveToState(Fragment.STARTED);
+    }
+    
+    public void dispatchStop() {
+        moveToState(Fragment.CREATED);
+    }
+    
+    public void dispatchDestroy() {
+        moveToState(Fragment.INITIALIZING);
+        mActivity = null;
+    }
+}
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
new file mode 100644
index 0000000..f97e510
--- /dev/null
+++ b/core/java/android/app/FragmentTransaction.java
@@ -0,0 +1,11 @@
+package android.app;
+
+/**
+ * API for performing a set of Fragment operations.
+ */
+public interface FragmentTransaction {
+    public FragmentTransaction add(Fragment fragment, int containerViewId);
+    public FragmentTransaction add(Fragment fragment, String name, int containerViewId);
+    public FragmentTransaction remove(Fragment fragment);
+    public void commit();
+}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b8c3aa3..7ed7c49 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1072,7 +1072,7 @@
           }
       }
       
-      activity.onDestroy();
+      activity.performDestroy();
       
       if (mActivityMonitors != null) {
           synchronized (mSync) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 30822d4..0afd6d2 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1372,7 +1372,6 @@
     public static final String SENSOR_SERVICE = "sensor";
     
     /**
-     * @hide
      * Use with {@link #getSystemService} to retrieve a {@link
      * android.os.storage.StorageManager} for accesssing system storage
      * functions.
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index a15e29e..5847216 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
@@ -69,6 +70,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} is called.
          * 
@@ -186,6 +198,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/SyncManager.java b/core/java/android/content/SyncManager.java
index 455815f..18f69af 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -16,6 +16,8 @@
 
 package android.content;
 
+import com.google.android.collect.Maps;
+
 import com.android.internal.R;
 import com.android.internal.util.ArrayUtils;
 
@@ -55,6 +57,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Random;
@@ -126,14 +129,13 @@
 
     private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000;
 
-    private static final String SYNC_WAKE_LOCK = "SyncManagerSyncWakeLock";
+    private static final String SYNC_WAKE_LOCK_PREFIX = "SyncWakeLock";
     private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock";
 
     private Context mContext;
 
     private volatile Account[] mAccounts = INITIAL_ACCOUNTS_ARRAY;
 
-    volatile private PowerManager.WakeLock mSyncWakeLock;
     volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
     volatile private boolean mDataConnectionIsConnected = false;
     volatile private boolean mStorageIsLow = false;
@@ -195,6 +197,8 @@
 
     private static final Account[] INITIAL_ACCOUNTS_ARRAY = new Account[0];
 
+    private final PowerManager mPowerManager;
+
     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;
@@ -356,15 +360,13 @@
         } 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);
 
@@ -1302,6 +1304,9 @@
         public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
         private Long mAlarmScheduleTime = null;
         public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
+        private PowerManager.WakeLock mSyncWakeLock;
+        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
@@ -1315,6 +1320,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) {
@@ -1477,8 +1494,9 @@
                 }
             } finally {
                 final boolean isSyncInProgress = mActiveSyncContext != null;
-                if (!isSyncInProgress) {
+                if (!isSyncInProgress && mSyncWakeLock != null) {
                     mSyncWakeLock.release();
+                    mSyncWakeLock = null;
                 }
                 manageSyncNotification();
                 manageErrorNotification();
@@ -1708,7 +1726,26 @@
                 return;
             }
 
-            mSyncWakeLock.acquire();
+            // Find the wakelock for this account and authority and store it in mSyncWakeLock.
+            // Be sure to release the previous wakelock so that we don't end up with it being
+            // held until it is used again.
+            // There are a couple tricky things about this code:
+            // - make sure that we acquire the new wakelock before releasing the old one,
+            //   otherwise the device might go to sleep as soon as we release it.
+            // - since we use non-reference counted wakelocks we have to be sure not to do
+            //   the release if the wakelock didn't change. Othewise we would do an
+            //   acquire followed by a release on the same lock, resulting in no lock
+            //   being held.
+            PowerManager.WakeLock oldWakeLock = mSyncWakeLock;
+            try {
+                mSyncWakeLock = getSyncWakeLock(op.account.type, op.authority);
+                mSyncWakeLock.acquire();
+            } finally {
+                if (oldWakeLock != null && oldWakeLock != mSyncWakeLock) {
+                    oldWakeLock.release();
+                }
+            }
+
             // 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
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index 038eedf..6f301da 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -16,15 +16,14 @@
 
 package android.database;
 
+import android.app.ActivityThread;
 import android.content.ContentResolver;
 import android.net.Uri;
 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 android.os.Process;
 
 import java.lang.ref.WeakReference;
 import java.lang.UnsupportedOperationException;
@@ -88,8 +87,16 @@
         }
         mDataSetObservable.notifyInvalidated();
     }
-    
+
     public boolean requery() {
+        // print a warning if this method is being called from Main (UI) thread
+        if (Looper.getMainLooper() == Looper.myLooper()) {
+            // yes this is UI thread.
+            String packageName = ActivityThread.currentPackageName();
+            Log.w(TAG, "should not attempt requery on main (UI) thread: app = " +
+                    packageName == null ? "'unknown'" : packageName,
+                    new RequeryOnUiThreadException(packageName));
+        }
         if (mSelfObserver != null && mSelfObserverRegistered == false) {
             mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
             mSelfObserverRegistered = true;
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index 79178f4..dc4471e 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -493,6 +493,10 @@
      * 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.
      */
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/DefaultDatabaseErrorHandler.java b/core/java/android/database/DefaultDatabaseErrorHandler.java
new file mode 100644
index 0000000..98aa54a
--- /dev/null
+++ b/core/java/android/database/DefaultDatabaseErrorHandler.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.database;
+
+import java.io.File;
+
+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).
+            if (!dbObj.getPath().equalsIgnoreCase(":memory")) {
+                // not memory database.
+                try {
+                    new File(dbObj.getPath()).delete();
+                } catch (Exception e) {
+                    /* ignore */
+                }
+            }
+            return;
+        }
+
+        try {
+            // Close the database, which will cause subsequent operations to fail.
+            try {
+                dbObj.close();
+            } catch (SQLiteException e) {
+                /* ignore */
+            }
+        } finally {
+            // Delete all files of this corrupt database and/or attached databases
+            for (Pair<String, String> p : dbObj.getAttachedDbs()) {
+                Log.e(TAG, "deleting the database file: " + p.second);
+                if (!p.second.equalsIgnoreCase(":memory:")) {
+                    // delete file if it is a non-memory database file
+                    try {
+                        new File(p.second).delete();
+                    } catch (Exception e) {
+                        /* ignore */
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/core/java/android/database/RequeryOnUiThreadException.java b/core/java/android/database/RequeryOnUiThreadException.java
new file mode 100644
index 0000000..97a50d8
--- /dev/null
+++ b/core/java/android/database/RequeryOnUiThreadException.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * An exception that indicates invoking {@link Cursor#requery()} on Main thread could cause ANR.
+ * This exception should encourage apps to invoke {@link Cursor#requery()} in a background thread. 
+ * @hide
+ */
+public class RequeryOnUiThreadException extends RuntimeException {
+    public RequeryOnUiThreadException(String packageName) {
+        super("In " + packageName + " Requery is executing on main (UI) thread. could cause ANR. " +
+                "do it in background thread.");
+    }
+}
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/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index 25aa9b3..d5c6ad1 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -134,6 +134,10 @@
         mInUse = false;
     }
 
+    /* package */ synchronized boolean isInUse() {
+        return mInUse;
+    }
+
     /**
      * Make sure that the native resource is cleaned up.
      */
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 70f681f..0a7eac2 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -16,12 +16,12 @@
 
 package android.database.sqlite;
 
-import com.google.android.collect.Maps;
-
 import android.app.ActivityThread;
 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;
@@ -35,11 +35,11 @@
 
 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;
@@ -251,7 +251,7 @@
     private 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
      *     value = {@link SQLiteCompiledSql}
@@ -263,15 +263,40 @@
      * 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;
+    /* package */ 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.
+                synchronized(mCompiledQueries) { // probably not necessary, but can't hurt
+                    if (this.size() <= mMaxSqlCacheSize) {
+                        // cache is not full. nothing needs to be removed
+                        return false;
+                    }
+                    // cache is full. eldest will be removed.
+                    SQLiteCompiledSql entry = eldest.getValue();
+                    if (!entry.isInUse()) {
+                        // this {@link SQLiteCompiledSql} is not in use. release it.
+                        entry.releaseSqlStatement();
+                    }
+                    // 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
+    public static final int MAX_SQL_CACHE_SIZE = 100;
     private int mCacheFullWarnings;
     private static final int MAX_WARNINGS_ON_CACHESIZE_CONDITION = 1;
 
@@ -279,10 +304,6 @@
     private int mNumCacheHits;
     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;
 
@@ -290,6 +311,11 @@
     private static final String LOG_SLOW_QUERIES_PROPERTY = "db.log.slow_query_threshold";
     private final int mSlowQueryThreshold;
 
+    /** {@link DatabaseErrorHandler} to be used when SQLite returns any of the following errors
+     *    Corruption
+     * */
+    private DatabaseErrorHandler errorHandler;
+
     /**
      * @param closable
      */
@@ -314,9 +340,6 @@
     @Override
     protected void onAllReferencesReleased() {
         if (isOpen()) {
-            if (SQLiteDebug.DEBUG_SQL_CACHE) {
-                mTimeClosed = getTime();
-            }
             dbclose();
         }
     }
@@ -347,19 +370,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();
-            }
-        }
+        errorHandler.onCorruption(this);
     }
 
     /**
@@ -811,10 +823,25 @@
      * @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());
+    }
+
+    /**
+     * same as {@link #openDatabase(String, CursorFactory, int)} except for an additional param
+     * errorHandler.
+     * @param errorHandler the {@link DatabaseErrorHandler} obj to be used when database
+     * corruption is detected on the database.
+     */
+    public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
+            DatabaseErrorHandler errorHandler) {
+        SQLiteDatabase sqliteDatabase = new SQLiteDatabase(path, factory, flags);
+
+        // set the ErrorHandler to be used when SQLite reports exceptions
+        sqliteDatabase.errorHandler = errorHandler;
+
         try {
             // Open the database.
-            sqliteDatabase = new SQLiteDatabase(path, factory, flags);
+            sqliteDatabase.openDatabase(path, flags);
             if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
                 sqliteDatabase.enableSqlTracing(path);
             }
@@ -822,14 +849,8 @@
                 sqliteDatabase.enableSqlProfiling(path);
             }
         } 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();
-            }
+            // Database is not even openable.
+            errorHandler.onCorruption(sqliteDatabase);
             sqliteDatabase = new SQLiteDatabase(path, factory, flags);
         }
         ActiveDatabases.getInstance().mActiveDatabases.add(
@@ -837,6 +858,18 @@
         return sqliteDatabase;
     }
 
+    private void openDatabase(String path, int flags) {
+        // Open the database.
+        dbopen(path, flags);
+        try {
+            setLocale(Locale.getDefault());
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Failed to setLocale(). closing the database", e);
+            dbclose();
+            throw e;
+        }
+    }
+
     /**
      * Equivalent to openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY).
      */
@@ -852,6 +885,17 @@
     }
 
     /**
+     * same as {@link #openOrCreateDatabase(String, CursorFactory)} except for an additional param
+     * errorHandler.
+     * @param errorHandler the {@link DatabaseErrorHandler} obj to be used when database
+     * corruption is detected on the database.
+     */
+    public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory,
+            DatabaseErrorHandler errorHandler) {
+        return openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler);
+    }
+
+    /**
      * Create a memory backed SQLite database.  Its contents will be destroyed
      * when the database is closed.
      *
@@ -1809,25 +1853,7 @@
         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());
     }
 
     /**
@@ -1968,14 +1994,6 @@
      * 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
@@ -1983,35 +2001,30 @@
             if (compiledSql != null) {
                 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.
+                 * log a warning.
+                 * chances are it is NOT using ? for bindargs - or cachesize is too small.
                  */
                 if (++mCacheFullWarnings == MAX_WARNINGS_ON_CACHESIZE_CONDITION) {
                     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");
+                            getPath() + ". Consider increasing cachesize.");
                 }
-                // 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);
-                }
+            } 
+            /* 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);
+            if (SQLiteDebug.DEBUG_SQL_CACHE) {
+                Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" +
+                        mCompiledQueries.size() + "|" + sql);
             }
         }
-        return;
     }
 
-
     private void deallocCachedSqlStatements() {
         synchronized (mCompiledQueries) {
             for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
@@ -2029,13 +2042,6 @@
         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());
-                }
-                return null;
-            }
             cacheHit = (compiledStatement = mCompiledQueries.get(sql)) != null;
         }
         if (cacheHit) {
@@ -2048,63 +2054,23 @@
             Log.v(TAG, "|cache_stats|" +
                     getPath() + "|" + mCompiledQueries.size() +
                     "|" + mNumCacheHits + "|" + mNumCacheMisses +
-                    "|" + cacheHit + "|" + mTimeOpened + "|" + mTimeClosed + "|" + sql);
+                    "|" + cacheHit + "|" + sql);
         }
         return compiledStatement;
     }
 
     /**
-     * returns true if the given sql is cached in compiled-sql cache.
-     * @hide
-     */
-    public boolean isInCompiledSqlCache(String sql) {
-        synchronized(mCompiledQueries) {
-            return mCompiledQueries.containsKey(sql);
-        }
-    }
-
-    /**
-     * purges the given sql from the compiled-sql cache.
-     * @hide
-     */
-    public void purgeFromCompiledSqlCache(String sql) {
-        synchronized(mCompiledQueries) {
-            mCompiledQueries.remove(sql);
-        }
-    }
-
-    /**
-     * remove everything from the compiled sql cache
-     * @hide
-     */
-    public void resetCompiledSqlCache() {
-        synchronized(mCompiledQueries) {
-            mCompiledQueries.clear();
-        }
-    }
-
-    /**
-     * return the current maxCacheSqlCacheSize
-     * @hide
-     */
-    public synchronized int getMaxSqlCacheSize() {
-        return mMaxSqlCacheSize;
-    }
-
-    /**
-     * set the max size of the compiled sql cache for this database after purging the cache.
+     * set the max size of the prepared-statement cache for this database.
      * (size of the cache = number of compiled-sql-statements stored in the cache).
      *
-     * max cache size can ONLY be increased from its current size (default = 0).
+     * max cache size can ONLY be increased from its current size (default = 10).
      * 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
+     * @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 synchronized void setMaxSqlCacheSize(int cacheSize) {
         if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
@@ -2144,7 +2110,7 @@
             String lastnode = path.substring((indx != -1) ? ++indx : 0);
 
             // get list of attached dbs and for each db, get its size and pagesize
-            ArrayList<Pair<String, String>> attachedDbs = getAttachedDbs(db);
+            ArrayList<Pair<String, String>> attachedDbs = db.getAttachedDbs();
             if (attachedDbs == null) {
                 continue;
             }
@@ -2169,7 +2135,8 @@
                 }
                 if (pageCount > 0) {
                     dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
-                            lookasideUsed));
+                            lookasideUsed, db.mNumCacheHits, db.mNumCacheMisses,
+                            db.mCompiledQueries.size()));
                 }
             }
         }
@@ -2197,24 +2164,74 @@
     }
 
     /**
-     * returns list of full pathnames of all attached databases
-     * including the main database
-     * TODO: move this to {@link DatabaseUtils}
+     * returns list of full pathnames of all attached databases including the main database
+     * @return ArrayList of pairs of (database name, database file path) or null if the database
+     * is not open.
      */
-    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)));
+        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();
+            }
         }
-        c.close();
         return attachedDbs;
     }
 
     /**
+     * run pragma integrity_check on the given database (and all the attached databases)
+     * and return true if the given database (and all its attached databases) pass integrity_check,
+     * false otherwise.
+     *
+     * if the result is false, then this method logs the errors reported by the integrity_check
+     * command execution.
+     *
+     * @return true if the given database (and all its attached databases) pass integrity_check,
+     * false otherwise
+     */
+    public boolean isDatabaseIntegrityOk() {
+        if (!isOpen()) {
+            throw new IllegalStateException("database: " + getPath() + " is NOT open");
+        }
+        ArrayList<Pair<String, String>> attachedDbs = getAttachedDbs();
+        if (attachedDbs == null) {
+            throw new IllegalStateException("databaselist for: " + getPath() + " couldn't " +
+                    "be retrieved. probably because the database is closed");
+        }
+        boolean isDatabaseCorrupt = false;
+        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
+                    isDatabaseCorrupt = true;
+                    Log.e(TAG, "PRAGMA integrity_check on " + p.second + " returned: " + rslt);
+                }
+            } finally {
+                if (prog != null) prog.close();
+            }
+        }
+        return isDatabaseCorrupt;
+    }
+
+    /**
      * Native call to open the database.
      *
      * @param path The full path to the database
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index a4db6d9..2970c9e 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -134,11 +134,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;
+            this.pageSize = pageSize / 1024;
             dbSize = (pageCount * pageSize) / 1024;
             this.lookaside = lookaside;
+            this.cache = hits + "/" + misses + "/" + cachesize;
         }
     }
 
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 52aac3a..d4907d9 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -99,6 +99,10 @@
             }
 
             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 {
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 89a5f0d1..a9c1ac6 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -147,7 +147,7 @@
      * @return a unique identifier for this program
      */
     public final int getUniqueId() {
-        return nStatement;
+        return (mCompiledSql != null) ? mCompiledSql.nStatement : 0;
     }
 
     /* package */ String getSqlString() {
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 a12603c..b49979c 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -45,8 +45,6 @@
  * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
  * of {@link android.content.Context#STORAGE_SERVICE}.
  *
- * @hide
- *
  */
 
 public class StorageManager
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 635323e..282417d 100644
--- a/core/java/android/pim/RecurrenceSet.java
+++ b/core/java/android/pim/RecurrenceSet.java
@@ -181,7 +181,9 @@
         boolean inUtc = start.parse(dtstart);
         boolean allDay = start.allDay;
 
-        if (inUtc) {
+        // We force TimeZone to UTC for "all day recurring events" as the server is sending no
+        // TimeZone in DTSTART for them
+        if (inUtc || allDay) {
             tzid = Time.TIMEZONE_UTC;
         }
                 
@@ -204,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
index 875c29e..dcfe980 100644
--- a/core/java/android/pim/vcard/JapaneseUtils.java
+++ b/core/java/android/pim/vcard/JapaneseUtils.java
@@ -27,7 +27,6 @@
         new HashMap<Character, String>();
 
     static {
-        // There's no logical mapping rule in Unicode. Sigh.
         sHalfWidthMap.put('\u3001', "\uFF64");
         sHalfWidthMap.put('\u3002', "\uFF61");
         sHalfWidthMap.put('\u300C', "\uFF62");
@@ -366,11 +365,11 @@
     }
 
     /**
-     * Return half-width version of that character if possible. Return null if not possible
+     * 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(char ch) {
+    public static String tryGetHalfWidthText(final char ch) {
         if (sHalfWidthMap.containsKey(ch)) {
             return sHalfWidthMap.get(ch);
         } else {
diff --git a/core/java/android/pim/vcard/VCardBuilder.java b/core/java/android/pim/vcard/VCardBuilder.java
index 0a6415d..789b5f8 100644
--- a/core/java/android/pim/vcard/VCardBuilder.java
+++ b/core/java/android/pim/vcard/VCardBuilder.java
@@ -47,7 +47,23 @@
 import java.util.Set;
 
 /**
- * The class which lets users create their own vCard String.
+ * <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";
@@ -75,13 +91,14 @@
     private static final String VCARD_WS = " ";
     private static final String VCARD_PARAM_EQUAL = "=";
 
-    private static final String VCARD_PARAM_ENCODING_QP = "ENCODING=QUOTED-PRINTABLE";
-
-    private static final String VCARD_PARAM_ENCODING_BASE64_V21 = "ENCODING=BASE64";
-    private static final String VCARD_PARAM_ENCODING_BASE64_V30 = "ENCODING=b";
+    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_V30 =
+            "ENCODING=" + VCardConstants.PARAM_ENCODING_B;
 
     private static final String SHIFT_JIS = "SHIFT_JIS";
-    private static final String UTF_8 = "UTF-8";
 
     private final int mVCardType;
 
@@ -92,21 +109,28 @@
     private final boolean mShouldUseQuotedPrintable;
     private final boolean mUsesAndroidProperty;
     private final boolean mUsesDefactProperty;
-    private final boolean mUsesUtf8;
-    private final boolean mUsesShiftJis;
     private final boolean mAppendTypeParamName;
     private final boolean mRefrainsQPToNameProperties;
     private final boolean mNeedsToConvertPhoneticString;
 
     private final boolean mShouldAppendCharsetParam;
 
-    private final String mCharsetString;
+    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.
+     */
+    public VCardBuilder(final int vcardType, String charset) {
         mVCardType = vcardType;
 
         mIsV30 = VCardConfig.isV30(vcardType);
@@ -116,40 +140,74 @@
         mOnlyOneNoteFieldIsAvailable = VCardConfig.onlyOneNoteFieldIsAvailable(vcardType);
         mUsesAndroidProperty = VCardConfig.usesAndroidSpecificProperty(vcardType);
         mUsesDefactProperty = VCardConfig.usesDefactProperty(vcardType);
-        mUsesUtf8 = VCardConfig.usesUtf8(vcardType);
-        mUsesShiftJis = VCardConfig.usesShiftJis(vcardType);
         mRefrainsQPToNameProperties = VCardConfig.shouldRefrainQPToNameProperties(vcardType);
         mAppendTypeParamName = VCardConfig.appendTypeParamName(vcardType);
         mNeedsToConvertPhoneticString = VCardConfig.needsToConvertPhoneticString(vcardType);
 
-        mShouldAppendCharsetParam = !(mIsV30 && mUsesUtf8);
+        // 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 = !(mIsV30 && "UTF-8".equalsIgnoreCase(charset));
 
-        if (mIsDoCoMo) {
-            String charset;
-            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;
+        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;
             }
-            mCharsetString = charset;
-            // Do not use mCharsetString bellow since it is different from "SHIFT_JIS" but
-            // may be "DOCOMO_SHIFT_JIS" or something like that (internal expression used in
-            // Android, not shown to the public).
-            mVCardCharsetParameter = "CHARSET=" + SHIFT_JIS;
-        } else if (mUsesShiftJis) {
-            String charset;
-            try {
-                charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
-            } catch (UnsupportedCharsetException e) {
-                Log.e(LOG_TAG, "Vendor-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
-                charset = SHIFT_JIS;
-            }
-            mCharsetString = charset;
             mVCardCharsetParameter = "CHARSET=" + SHIFT_JIS;
         } else {
-            mCharsetString = UTF_8;
-            mVCardCharsetParameter = "CHARSET=" + UTF_8;
+            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();
     }
@@ -379,8 +437,8 @@
             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 for external importers, assuming no external
-            //       importer allows this vioration.
+            //       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);
@@ -454,18 +512,18 @@
             mBuilder.append(VCARD_END_OF_LINE);
         } else if (mIsJapaneseMobilePhone) {
             // Note: There is no appropriate property for expressing
-            //       phonetic name in vCard 2.1, while there is in
+            //       phonetic name (Yomigana in Japanese) in vCard 2.1, while there is in
             //       vCard 3.0 (SORT-STRING).
-            //       We chose to use DoCoMo's way when the device is Japanese one
-            //       since it is supported by
-            //       a lot of Japanese mobile phones. This is "X-" property, so
-            //       any parser hopefully would not get confused with this.
+            //       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.
-            //       o  SOUND;X-IRMC-N:Miyakawa Daisuke;;;;
-            //       x  SOUND;X-IRMC-N:Miyakawa;Daisuke;;;
+            //       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);
@@ -519,10 +577,10 @@
                     mBuilder.append(encodedPhoneticGivenName);
                 }
             }
-            mBuilder.append(VCARD_ITEM_SEPARATOR);
-            mBuilder.append(VCARD_ITEM_SEPARATOR);
-            mBuilder.append(VCARD_ITEM_SEPARATOR);
-            mBuilder.append(VCARD_ITEM_SEPARATOR);
+            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);
         }
 
@@ -549,7 +607,7 @@
                 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 &&
@@ -572,7 +630,7 @@
                 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 &&
@@ -595,7 +653,7 @@
                 mBuilder.append(VCARD_DATA_SEPARATOR);
                 mBuilder.append(encodedPhoneticFamilyName);
                 mBuilder.append(VCARD_END_OF_LINE);
-            }
+            }  // if (!TextUtils.isEmpty(phoneticFamilyName))
         }
     }
 
@@ -642,22 +700,18 @@
                 if (TextUtils.isEmpty(phoneNumber)) {
                     continue;
                 }
-                int type = (typeAsObject != null ? typeAsObject : DEFAULT_PHONE_TYPE);
-                if (type == Phone.TYPE_PAGER) {
+
+                // 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 {
-                    // The entry "may" have several phone numbers when the contact entry is
-                    // corrupted because of its original source.
-                    //
-                    // e.g. I encountered the entry like the following.
-                    // "111-222-3333 (Miami)\n444-555-6666 (Broward; 305-653-6796 (Miami); ..."
-                    // This kind of entry is not able to be inserted via Android devices, but
-                    // possible if the source of the data is already corrupted.
-                    List<String> phoneNumberList = splitIfSeveralPhoneNumbersExist(phoneNumber);
+                    final List<String> phoneNumberList = splitAndTrimPhoneNumbers(phoneNumber);
                     if (phoneNumberList.isEmpty()) {
                         continue;
                     }
@@ -670,7 +724,7 @@
                             phoneSet.add(actualPhoneNumber);
                             appendTelLine(type, label, formattedPhoneNumber, isPrimary);
                         }
-                    }
+                    }  // for (String actualPhoneNumber : phoneNumberList) {
                 }
             }
         }
@@ -682,15 +736,38 @@
         return this;
     }
 
-    private List<String> splitIfSeveralPhoneNumbersExist(final String phoneNumber) {
-        List<String> phoneList = new ArrayList<String>();
+    /**
+     * <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);
-            // TODO: add a test case for string with '+', and care the other possible issues
-            // which may happen by ignoring non-digits other than '+'.
             if (Character.isDigit(ch) || ch == '+') {
                 builder.append(ch);
             } else if ((ch == ';' || ch == '\n') && builder.length() > 0) {
@@ -903,21 +980,21 @@
                 encodedCountry = escapeCharacters(rawCountry);
                 encodedNeighborhood = escapeCharacters(rawNeighborhood);
             }
-            final StringBuffer addressBuffer = new StringBuffer();
-            addressBuffer.append(encodedPoBox);
-            addressBuffer.append(VCARD_ITEM_SEPARATOR);
-            addressBuffer.append(VCARD_ITEM_SEPARATOR);
-            addressBuffer.append(encodedStreet);
-            addressBuffer.append(VCARD_ITEM_SEPARATOR);
-            addressBuffer.append(encodedLocality);
-            addressBuffer.append(VCARD_ITEM_SEPARATOR);
-            addressBuffer.append(encodedRegion);
-            addressBuffer.append(VCARD_ITEM_SEPARATOR);
-            addressBuffer.append(encodedPostalCode);
-            addressBuffer.append(VCARD_ITEM_SEPARATOR);
-            addressBuffer.append(encodedCountry);
+            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, addressBuffer.toString());
+                    reallyUseQuotedPrintable, appendCharset, addressBuilder.toString());
         } else {  // VCardUtils.areAllEmpty(rawAddressArray) == true
             // Try to use FORMATTED_ADDRESS instead.
             final String rawFormattedAddress =
@@ -940,16 +1017,16 @@
             // 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 StringBuffer addressBuffer = new StringBuffer();
-            addressBuffer.append(VCARD_ITEM_SEPARATOR);
-            addressBuffer.append(encodedFormattedAddress);
-            addressBuffer.append(VCARD_ITEM_SEPARATOR);
-            addressBuffer.append(VCARD_ITEM_SEPARATOR);
-            addressBuffer.append(VCARD_ITEM_SEPARATOR);
-            addressBuffer.append(VCARD_ITEM_SEPARATOR);
-            addressBuffer.append(VCARD_ITEM_SEPARATOR);
+            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, addressBuffer.toString());
+                    reallyUseQuotedPrintable, appendCharset, addressBuilder.toString());
         }
     }
 
@@ -1146,6 +1223,8 @@
     }
 
     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;
@@ -1213,16 +1292,19 @@
         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 emitLineEveryTime) {
+            final boolean isPrimary, final boolean emitEveryTime) {
         final boolean reallyUseQuotedPrintable;
         final boolean appendCharset;
         final String addressValue;
         {
             PostalStruct postalStruct = tryConstructPostalStruct(contentValues);
             if (postalStruct == null) {
-                if (emitLineEveryTime) {
+                if (emitEveryTime) {
                     reallyUseQuotedPrintable = false;
                     appendCharset = false;
                     addressValue = "";
@@ -1537,7 +1619,8 @@
         mBuilder.append(VCARD_END_OF_LINE);
     }
 
-    public void appendAndroidSpecificProperty(final String mimeType, ContentValues contentValues) {
+    public void appendAndroidSpecificProperty(
+            final String mimeType, ContentValues contentValues) {
         if (!sAllowedAndroidPropertySet.contains(mimeType)) {
             return;
         }
@@ -1659,7 +1742,7 @@
             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 this.
+            //       several (even well-known) applications do not care that violation.
             encodedValue = escapeCharacters(rawValue);
         }
 
@@ -1794,9 +1877,9 @@
         byte[] strArray = null;
 
         try {
-            strArray = str.getBytes(mCharsetString);
+            strArray = str.getBytes(mCharset);
         } catch (UnsupportedEncodingException e) {
-            Log.e(LOG_TAG, "Charset " + mCharsetString + " cannot be used. "
+            Log.e(LOG_TAG, "Charset " + mCharset + " cannot be used. "
                     + "Try default charset");
             strArray = str.getBytes();
         }
diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java
index 0e8b665..170d6fa 100644
--- a/core/java/android/pim/vcard/VCardComposer.java
+++ b/core/java/android/pim/vcard/VCardComposer.java
@@ -41,6 +41,7 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.text.TextUtils;
 import android.util.CharsetUtils;
 import android.util.Log;
 
@@ -61,15 +62,11 @@
 
 /**
  * <p>
- * The class for composing VCard from Contacts information. Note that this is
- * completely differnt implementation from
- * android.syncml.pim.vcard.VCardComposer, which is not maintained anymore.
+ * 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);
@@ -93,15 +90,18 @@
  *     if (composer != null) {
  *         composer.terminate();
  *     }
- * } </pre>
+ * }</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 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;
-
     public static final String FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO =
         "Failed to get database information";
 
@@ -119,6 +119,8 @@
 
     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";
 
@@ -141,7 +143,7 @@
         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);
-        // Google talk is a special case.
+        // We don't add Google talk here since it has to be handled separately.
     }
 
     public static interface OneEntryHandler {
@@ -152,37 +154,37 @@
 
     /**
      * <p>
-     * An useful example handler, which emits VCard String to outputstream one by one.
+     * 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.
+     * Must not close the stream outside this class.
      * </p>
      */
-    public class HandlerForOutputStream implements OneEntryHandler {
+    public final class HandlerForOutputStream implements OneEntryHandler {
         @SuppressWarnings("hiding")
-        private static final String LOG_TAG = "vcard.VCardComposer.HandlerForOutputStream";
-
-        final private OutputStream mOutputStream; // mWriter will close this.
-        private Writer mWriter;
+        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(OutputStream outputStream) {
+        public HandlerForOutputStream(final OutputStream outputStream) {
             mOutputStream = outputStream;
         }
 
-        public boolean onInit(Context context) {
+        public boolean onInit(final Context context) {
             try {
                 mWriter = new BufferedWriter(new OutputStreamWriter(
-                        mOutputStream, mCharsetString));
+                        mOutputStream, mCharset));
             } catch (UnsupportedEncodingException e1) {
-                Log.e(LOG_TAG, "Unsupported charset: " + mCharsetString);
+                Log.e(LOG_TAG, "Unsupported charset: " + mCharset);
                 mErrorReason = "Encoding is not supported (usually this does not happen!): "
-                        + mCharsetString;
+                        + mCharset;
                 return false;
             }
 
@@ -235,14 +237,19 @@
                             "IOException during closing the output stream: "
                                     + e.getMessage());
                 } finally {
-                    try {
-                        mWriter.close();
-                    } catch (IOException e) {
-                    }
+                    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) {
@@ -257,11 +264,10 @@
     private final ContentResolver mContentResolver;
 
     private final boolean mIsDoCoMo;
-    private final boolean mUsesShiftJis;
     private Cursor mCursor;
     private int mIdColumn;
 
-    private final String mCharsetString;
+    private final String mCharset;
     private boolean mTerminateIsCalled;
     private final List<OneEntryHandler> mHandlerList;
 
@@ -272,21 +278,39 @@
     };
 
     public VCardComposer(Context context) {
-        this(context, VCardConfig.VCARD_TYPE_DEFAULT, true);
+        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, true);
+        this(context, vcardType, null, true);
     }
 
-    public VCardComposer(Context context, String vcardTypeStr, boolean careHandlerErrors) {
-        this(context, VCardConfig.getVCardTypeFromString(vcardTypeStr), careHandlerErrors);
+    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,
+    public VCardComposer(final Context context, final int vcardType, String charset,
             final boolean careHandlerErrors) {
         mContext = context;
         mVCardType = vcardType;
@@ -294,30 +318,67 @@
         mContentResolver = context.getContentResolver();
 
         mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
-        mUsesShiftJis = VCardConfig.usesShiftJis(vcardType);
         mHandlerList = new ArrayList<OneEntryHandler>();
 
-        if (mIsDoCoMo) {
-            String charset;
-            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;
+        charset = (TextUtils.isEmpty(charset) ? VCardConfig.DEFAULT_EXPORT_CHARSET : charset);
+        final boolean shouldAppendCharsetParam = !(
+                VCardConfig.isV30(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;
+                }
             }
-            mCharsetString = charset;
-        } else if (mUsesShiftJis) {
-            String charset;
-            try {
-                charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
-            } catch (UnsupportedCharsetException e) {
-                Log.e(LOG_TAG, "Vendor-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
-                charset = SHIFT_JIS;
-            }
-            mCharsetString = charset;
         } else {
-            mCharsetString = UTF_8;
+            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 + "\"");
     }
 
     /**
@@ -351,7 +412,7 @@
         }
 
         if (mCareHandlerErrors) {
-            List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
+            final List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
                     mHandlerList.size());
             for (OneEntryHandler handler : mHandlerList) {
                 if (!handler.onInit(mContext)) {
@@ -414,7 +475,7 @@
             mErrorReason = FAILURE_REASON_NOT_INITIALIZED;
             return false;
         }
-        String vcard;
+        final String vcard;
         try {
             if (mIdColumn >= 0) {
                 vcard = createOneEntryInternal(mCursor.getString(mIdColumn),
@@ -437,8 +498,7 @@
             mCursor.moveToNext();
         }
 
-        // This function does not care the OutOfMemoryError on the handler side
-        // :-P
+        // This function does not care the OutOfMemoryError on the handler side :-P
         if (mCareHandlerErrors) {
             List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
                     mHandlerList.size());
@@ -457,7 +517,7 @@
     }
 
     private String createOneEntryInternal(final String contactId,
-            Method getEntityIteratorMethod) throws VCardException {
+            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.
@@ -471,7 +531,7 @@
             final String selection = Data.CONTACT_ID + "=?";
             final String[] selectionArgs = new String[] {contactId};
             if (getEntityIteratorMethod != null) {
-                // Please note that this branch is executed by some tests only
+                // Please note that this branch is executed by unit tests only
                 try {
                     entityIterator = (EntityIterator)getEntityIteratorMethod.invoke(null,
                             mContentResolver, uri, selection, selectionArgs, null);
@@ -527,22 +587,33 @@
             }
         }
 
-        final VCardBuilder builder = new VCardBuilder(mVCardType);
-        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));
+        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))
+                    .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();
         }
-        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() {
@@ -565,26 +636,38 @@
     @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 Return the error reason if possible.
+     * @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
index 3409be6..6c25216 100644
--- a/core/java/android/pim/vcard/VCardConfig.java
+++ b/core/java/android/pim/vcard/VCardConfig.java
@@ -15,6 +15,7 @@
  */
 package android.pim.vcard;
 
+import android.telephony.PhoneNumberUtils;
 import android.util.Log;
 
 import java.util.HashMap;
@@ -37,16 +38,32 @@
 
     /* package */ static final int LOG_LEVEL = LOG_LEVEL_NONE;
 
-    /* package */ static final int PARSE_TYPE_UNKNOWN = 0;
-    /* package */ static final int PARSE_TYPE_APPLE = 1;
-    /* package */ static final int PARSE_TYPE_MOBILE_PHONE_JP = 2;  // For Japanese mobile phones.
-    /* package */ static final int PARSE_TYPE_FOMA = 3;  // For Japanese FOMA mobile phones.
-    /* package */ static final int PARSE_TYPE_WINDOWS_MOBILE_JP = 4;
+    /**
+     * <p>
+     * The charset used during import.
+     * </p>
+     * <p>
+     * We cannot determine which charset should be used to interpret a given vCard file
+     * at first, while we have to decode sime encoded data (e.g. BASE64) to binary.
+     * In order to avoid "misinterpretation" of charset as much as possible,
+     * "ISO-8859-1" (a.k.a Latin-1) is first used for reading a stream.
+     * When charset is specified in a property (with "CHARSET=..." parameter),
+     * the string is decoded to raw bytes and encoded into the specific charset,
+     * assuming "ISO-8859-1" is able to map "all" 8bit characters to some unicode,
+     * and it has 1 to 1 mapping in all 8bit characters.
+     * If the assumption is not correct, this setting will cause some bug.
+     * </p>
+     * @hide made public just for unit test
+     */
+    public static final String DEFAULT_INTERMEDIATE_CHARSET = "ISO-8859-1";
 
-    // Assumes that "iso-8859-1" is able to map "all" 8bit characters to some unicode and
-    // decode the unicode to the original charset. If not, this setting will cause some bug. 
-    public static final String DEFAULT_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";
+
     public static final int FLAG_V21 = 0;
     public static final int FLAG_V30 = 1;
 
@@ -58,311 +75,329 @@
     private static final int NAME_ORDER_MASK = 0xC;
 
     // 0x10 is reserved for safety
-    
-    private static final int FLAG_CHARSET_UTF8 = 0;
-    private static final int FLAG_CHARSET_SHIFT_JIS = 0x100;
-    private static final int FLAG_CHARSET_MASK = 0xF00;
 
     /**
+     * <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.
-     * 
-     * Note that this flag does not assure all the information must be hold in the emitted vCard.
+     * </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. 
-     * 
+     * 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;
 
     /**
-     * The flag indicating some specific dialect seen in vcard of DoCoMo (one of Japanese
+     * <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>
+     * <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>
+     * </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>
+     * </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>
+     * <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>
+     * </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>
+     * </p>
+     * <p>
      * Again, we should not use this flag at all for complying vCard 2.1 spec.
-     * </P>
-     * <P>
+     * </p>
+     * <p>
      * In vCard 3.0, Quoted-Printable is explicitly "prohibitted", so we don't need to care this
      * kind of problem (hopefully).
-     * </P>
+     * </p>
+     * @hide
      */
     public static final int FLAG_REFRAIN_QP_TO_NAME_PROPERTIES = 0x10000000;
 
     /**
-     * <P>
+     * <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>
+     * </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>
+     * </p>
      */
-    public static final int FLAG_CONVERT_PHONETIC_NAME_STRINGS = 0x0800000;
+    public static final int FLAG_CONVERT_PHONETIC_NAME_STRINGS = 0x08000000;
 
     /**
-     * <P>
+     * <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>
+     * </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.<BR />
-     * 1) Probably valid in both vCard 2.1 and vCard 3.0: "ADR;TYPE=DOM;TYPE=HOME:..."<BR />
-     * 2) Valid in vCard 2.1 but not in vCard 3.0: "ADR;DOM;HOME:..."<BR />
-     * 3) Valid in vCard 3.0 but not in vCard 2.1: "ADR;TYPE=DOM,HOME:..."<BR />
-     * </P>
-     * <P>
-     * 2) had been the default of VCard exporter/importer in Android, but it is found that
-     * some external exporter is not able to parse the type format like 2) but only 3).
-     * </P>
-     * <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: int vcardType = (VCARD_TYPE_V21_GENERIC | FLAG_APPEND_TYPE_PARAM);
-     * </P>
+     * </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 asking exporter to refrain image export.
-     * </P>
-     * @hide will be deleted in the near future.
+     * <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_IMAGE_EXPORT = 0x02000000;
+    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. ////
 
     /**
-     * <P>
-     * Generic vCard format with the vCard 2.1. Uses UTF-8 for the charset.
-     * When composing a vCard entry, the US convension will be used toward formatting
-     * some values.
-     * </P>
-     * <P>
+     * <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>
+     * <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_UTF8 =
-        (FLAG_V21 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
-                FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+    public static final int VCARD_TYPE_V21_GENERIC =
+        (FLAG_V21 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
 
-    /* package */ static String VCARD_TYPE_V21_GENERIC_UTF8_STR = "v21_generic";
+    /* package */ static String VCARD_TYPE_V21_GENERIC_STR = "v21_generic";
     
     /**
-     * <P>
+     * <p>
      * General vCard format with the version 3.0. Uses UTF-8 for the charset.
-     * </P>
-     * <P>
+     * </p>
+     * <p>
      * Not fully ready yet. Use with caution when you use this.
-     * </P>
+     * </p>
      */
-    public static final int VCARD_TYPE_V30_GENERIC_UTF8 =
-        (FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
-                FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+    public static final int VCARD_TYPE_V30_GENERIC =
+        (FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
 
-    /* package */ static final String VCARD_TYPE_V30_GENERIC_UTF8_STR = "v30_generic";
+    /* package */ static final String VCARD_TYPE_V30_GENERIC_STR = "v30_generic";
     
     /**
-     * <P>
+     * <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>
+     * </p>
      */
-    public static final int VCARD_TYPE_V21_EUROPE_UTF8 =
-        (FLAG_V21 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 |
-                FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
-    
-    /* package */ static final String VCARD_TYPE_V21_EUROPE_UTF8_STR = "v21_europe";
+    public static final int VCARD_TYPE_V21_EUROPE =
+        (FLAG_V21 | NAME_ORDER_EUROPE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+
+    /* package */ static final String VCARD_TYPE_V21_EUROPE_STR = "v21_europe";
     
     /**
-     * <P>
+     * <p>
      * General vCard format with the version 3.0 with some Europe convension. Uses UTF-8.
-     * </P>
-     * <P>
+     * </p>
+     * <p>
      * Not ready yet. Use with caution when you use this.
-     * </P>
+     * </p>
      */
-    public static final int VCARD_TYPE_V30_EUROPE_UTF8 =
-        (FLAG_V30 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 |
-                FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+    public static final int VCARD_TYPE_V30_EUROPE =
+        (FLAG_V30 | NAME_ORDER_EUROPE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
     
     /* package */ static final String VCARD_TYPE_V30_EUROPE_STR = "v30_europe";
 
     /**
-     * <P>
+     * <p>
      * The vCard 2.1 format for miscellaneous Japanese devices, using UTF-8 as default charset.
-     * </P>
-     * <P>
+     * </p>
+     * <p>
      * Not ready yet. Use with caution when you use this.
-     * </P>
+     * </p>
      */
-    public static final int VCARD_TYPE_V21_JAPANESE_UTF8 =
-        (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
-                FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+    public static final int VCARD_TYPE_V21_JAPANESE =
+        (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
 
-    /* package */ static final String VCARD_TYPE_V21_JAPANESE_UTF8_STR = "v21_japanese_utf8";
+    /* package */ static final String VCARD_TYPE_V21_JAPANESE_STR = "v21_japanese_utf8";
 
     /**
-     * <P>
-     * vCard 2.1 format for miscellaneous Japanese devices. Shift_Jis is used for
-     * parsing/composing the vCard data.
-     * </P>
-     * <P>
-     * Not ready yet. Use with caution when you use this.
-     * </P>
-     */
-    public static final int VCARD_TYPE_V21_JAPANESE_SJIS =
-        (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
-                FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
-
-    /* package */ static final String VCARD_TYPE_V21_JAPANESE_SJIS_STR = "v21_japanese_sjis";
-    
-    /**
-     * <P>
-     * vCard format for miscellaneous Japanese devices, using Shift_Jis for
-     * parsing/composing the vCard data.
-     * </P>
-     * <P>
-     * Not ready yet. Use with caution when you use this.
-     * </P>
-     */
-    public static final int VCARD_TYPE_V30_JAPANESE_SJIS =
-        (FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
-                FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
-        
-    /* package */ static final String VCARD_TYPE_V30_JAPANESE_SJIS_STR = "v30_japanese_sjis";
-    
-    /**
-     * <P>
+     * <p>
      * The vCard 3.0 format for miscellaneous Japanese devices, using UTF-8 as default charset.
-     * </P>
-     * <P>
+     * </p>
+     * <p>
      * Not ready yet. Use with caution when you use this.
-     * </P>
+     * </p>
      */
-    public static final int VCARD_TYPE_V30_JAPANESE_UTF8 =
-        (FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
-                FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+    public static final int VCARD_TYPE_V30_JAPANESE =
+        (FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
 
-    /* package */ static final String VCARD_TYPE_V30_JAPANESE_UTF8_STR = "v30_japanese_utf8";
+    /* package */ static final String VCARD_TYPE_V30_JAPANESE_STR = "v30_japanese_utf8";
 
     /**
-     * <P>
+     * <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.
-     * </P>
-     * <P>
-     * Not ready yet. Use with caution when you use this.
-     * </P>
+     * 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 =
-        (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
-                FLAG_CONVERT_PHONETIC_NAME_STRINGS |
-                FLAG_REFRAIN_QP_TO_NAME_PROPERTIES);
+        (FLAG_V21 | 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>
-     * VCard format used in DoCoMo, which is one of Japanese mobile phone careers.
+     * <p>
+     * The vCard format used in DoCoMo, which is one of Japanese mobile phone careers.
      * </p>
-     * <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>
+     * </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_UTF8;
+    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_UTF8_STR, VCARD_TYPE_V21_GENERIC_UTF8);
-        sVCardTypeMap.put(VCARD_TYPE_V30_GENERIC_UTF8_STR, VCARD_TYPE_V30_GENERIC_UTF8);
-        sVCardTypeMap.put(VCARD_TYPE_V21_EUROPE_UTF8_STR, VCARD_TYPE_V21_EUROPE_UTF8);
-        sVCardTypeMap.put(VCARD_TYPE_V30_EUROPE_STR, VCARD_TYPE_V30_EUROPE_UTF8);
-        sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_SJIS_STR, VCARD_TYPE_V21_JAPANESE_SJIS);
-        sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_UTF8_STR, VCARD_TYPE_V21_JAPANESE_UTF8);
-        sVCardTypeMap.put(VCARD_TYPE_V30_JAPANESE_SJIS_STR, VCARD_TYPE_V30_JAPANESE_SJIS);
-        sVCardTypeMap.put(VCARD_TYPE_V30_JAPANESE_UTF8_STR, VCARD_TYPE_V30_JAPANESE_UTF8);
+        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_SJIS);
-        sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_UTF8);
-        sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_SJIS);
-        sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE_SJIS);
-        sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE_UTF8);
+        sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE);
+        sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE);
         sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_MOBILE);
         sJapaneseMobileTypeSet.add(VCARD_TYPE_DOCOMO);
     }
@@ -387,14 +422,6 @@
         return !isV30(vcardType);
     }
 
-    public static boolean usesUtf8(final int vcardType) {
-        return ((vcardType & FLAG_CHARSET_MASK) == FLAG_CHARSET_UTF8);
-    }
-
-    public static boolean usesShiftJis(final int vcardType) {
-        return ((vcardType & FLAG_CHARSET_MASK) == FLAG_CHARSET_SHIFT_JIS);
-    }
-
     public static int getNameOrderType(final int vcardType) {
         return vcardType & NAME_ORDER_MASK;
     }
@@ -431,6 +458,10 @@
         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);
     }
diff --git a/core/java/android/pim/vcard/VCardConstants.java b/core/java/android/pim/vcard/VCardConstants.java
index 8c07126..e11b1fd 100644
--- a/core/java/android/pim/vcard/VCardConstants.java
+++ b/core/java/android/pim/vcard/VCardConstants.java
@@ -109,6 +109,12 @@
     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.
@@ -130,10 +136,6 @@
     // Do not use in composer side.
     public static final String PARAM_EXTRA_TYPE_COMPANY = "COMPANY";
 
-    // DoCoMo specific type parameter. Used with "SOUND" property, which is alternate of SORT-STRING in
-    // vCard 3.0.
-    public static final String PARAM_TYPE_X_IRMC_N = "X-IRMC-N";
-
     public interface ImportOnly {
         public static final String PROPERTY_X_NICKNAME = "X-NICKNAME";
         // Some device emits this "X-" parameter for expressing Google Talk,
@@ -142,6 +144,12 @@
         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";
+
     /* package */ static final int MAX_DATA_COLUMN = 15;
 
     /* package */ static final int MAX_CHARACTER_NUMS_QP = 76;
diff --git a/core/java/android/pim/vcard/VCardEntry.java b/core/java/android/pim/vcard/VCardEntry.java
index 1327770..5b9cf17 100644
--- a/core/java/android/pim/vcard/VCardEntry.java
+++ b/core/java/android/pim/vcard/VCardEntry.java
@@ -61,9 +61,6 @@
 
     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 {
@@ -78,12 +75,12 @@
                 Im.PROTOCOL_GOOGLE_TALK);
     }
 
-    static public class PhoneData {
+    public static class PhoneData {
         public final int type;
         public final String data;
         public final String label;
-        // isPrimary is changable only when there's no appropriate one existing in
-        // the original VCard.
+        // 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;
@@ -109,13 +106,11 @@
         }
     }
 
-    static public class EmailData {
+    public static class EmailData {
         public final int type;
         public final String data;
         // Used only when TYPE is TYPE_CUSTOM.
         public final String label;
-        // isPrimary is changable only when there's no appropriate one existing in
-        // the original VCard.
         public boolean isPrimary;
         public EmailData(int type, String data, String label, boolean isPrimary) {
             this.type = type;
@@ -141,9 +136,9 @@
         }
     }
 
-    static public class PostalData {
-        // Determined by vCard spec.
-        // PO Box, Extended Addr, Street, Locality, Region, Postal Code, Country Name
+    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;
@@ -248,10 +243,11 @@
         }
     }
 
-    static public class OrganizationData {
+    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".
+        // 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;
@@ -313,7 +309,7 @@
         }
     }
 
-    static public class ImData {
+    public static class ImData {
         public final int protocol;
         public final String customProtocol;
         public final int type;
@@ -441,7 +437,7 @@
     private String mSuffix;
 
     // Used only when no family nor given name is found.
-    private String mFullName;
+    private String mFormattedName;
 
     private String mPhoneticFamilyName;
     private String mPhoneticGivenName;
@@ -469,7 +465,7 @@
     private final Account mAccount;
 
     public VCardEntry() {
-        this(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+        this(VCardConfig.VCARD_TYPE_V21_GENERIC);
     }
 
     public VCardEntry(int vcardType) {
@@ -488,7 +484,7 @@
         final StringBuilder builder = new StringBuilder();
         final String trimed = data.trim();
         final String formattedNumber;
-        if (type == Phone.TYPE_PAGER) {
+        if (type == Phone.TYPE_PAGER || VCardConfig.refrainPhoneNumberFormatting(mVCardType)) {
             formattedNumber = trimed;
         } else {
             final int length = trimed.length();
@@ -499,9 +495,7 @@
                 }
             }
 
-            // Use NANP in default when there's no information about locale.
-            final int formattingType = (VCardConfig.isJapaneseDevice(mVCardType) ?
-                    PhoneNumberUtils.FORMAT_JAPAN : PhoneNumberUtils.FORMAT_NANP);
+            final int formattingType = VCardUtils.getPhoneNumberFormat(mVCardType);
             formattedNumber = PhoneNumberUtils.formatNumber(builder.toString(), formattingType);
         }
         PhoneData phoneData = new PhoneData(type, formattedNumber, label, isPrimary);
@@ -755,11 +749,11 @@
         if (propName.equals(VCardConstants.PROPERTY_VERSION)) {
             // vCard version. Ignore this.
         } else if (propName.equals(VCardConstants.PROPERTY_FN)) {
-            mFullName = propValue;
-        } else if (propName.equals(VCardConstants.PROPERTY_NAME) && mFullName == null) {
+            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.
-            mFullName = propValue;
+            mFormattedName = propValue;
         } else if (propName.equals(VCardConstants.PROPERTY_N)) {
             handleNProperty(propValueList);
         } else if (propName.equals(VCardConstants.PROPERTY_SORT_STRING)) {
@@ -1017,8 +1011,8 @@
      */
     private void constructDisplayName() {
         // FullName (created via "FN" or "NAME" field) is prefered.
-        if (!TextUtils.isEmpty(mFullName)) {
-            mDisplayName = mFullName;
+        if (!TextUtils.isEmpty(mFormattedName)) {
+            mDisplayName = mFormattedName;
         } else if (!(TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mGivenName))) {
             mDisplayName = VCardUtils.constructNameFromElements(mVCardType,
                     mFamilyName, mMiddleName, mGivenName, mPrefix, mSuffix);
@@ -1063,23 +1057,6 @@
         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);
@@ -1321,7 +1298,7 @@
                 && TextUtils.isEmpty(mGivenName)
                 && TextUtils.isEmpty(mPrefix)
                 && TextUtils.isEmpty(mSuffix)
-                && TextUtils.isEmpty(mFullName)
+                && TextUtils.isEmpty(mFormattedName)
                 && TextUtils.isEmpty(mPhoneticFamilyName)
                 && TextUtils.isEmpty(mPhoneticMiddleName)
                 && TextUtils.isEmpty(mPhoneticGivenName)
@@ -1380,7 +1357,7 @@
     }
 
     public String getFullName() {
-        return mFullName;
+        return mFormattedName;
     }
 
     public String getPhoneticFamilyName() {
diff --git a/core/java/android/pim/vcard/VCardEntryCommitter.java b/core/java/android/pim/vcard/VCardEntryCommitter.java
index 59a2baf..a8c8057 100644
--- a/core/java/android/pim/vcard/VCardEntryCommitter.java
+++ b/core/java/android/pim/vcard/VCardEntryCommitter.java
@@ -52,9 +52,9 @@
         }
     }
 
-    public void onEntryCreated(final VCardEntry contactStruct) {
+    public void onEntryCreated(final VCardEntry vcardEntry) {
         long start = System.currentTimeMillis();
-        mCreatedUris.add(contactStruct.pushIntoContentResolver(mContentResolver));
+        mCreatedUris.add(vcardEntry.pushIntoContentResolver(mContentResolver));
         mTimeToCommit += System.currentTimeMillis() - start;
     }
 
diff --git a/core/java/android/pim/vcard/VCardEntryConstructor.java b/core/java/android/pim/vcard/VCardEntryConstructor.java
index 290ca2b..a0abae8 100644
--- a/core/java/android/pim/vcard/VCardEntryConstructor.java
+++ b/core/java/android/pim/vcard/VCardEntryConstructor.java
@@ -16,12 +16,11 @@
 package android.pim.vcard;
 
 import android.accounts.Account;
+import android.text.TextUtils;
 import android.util.CharsetUtils;
 import android.util.Log;
 
-import org.apache.commons.codec.DecoderException;
 import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.codec.net.QuotedPrintableCodec;
 
 import java.io.UnsupportedEncodingException;
 import java.nio.ByteBuffer;
@@ -30,64 +29,73 @@
 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";
 
-    /**
-     * If there's no other information available, this class uses this charset for encoding
-     * byte arrays to String.
-     */
-    /* package */ static final String DEFAULT_CHARSET_FOR_DECODED_BYTES = "UTF-8";
-
     private VCardEntry.Property mCurrentProperty = new VCardEntry.Property();
-    private VCardEntry mCurrentContactStruct;
+    private VCardEntry mCurrentVCardEntry;
     private String mParamType;
     
-    /**
-     * The charset using which {@link VCardInterpreter} parses the text.
-     */
-    private String mInputCharset;
+    // 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;
 
-    /**
-     * The charset with which byte array is encoded to String.
-     */
-    final private String mCharsetForDecodedBytes;
-    final private boolean mStrictLineBreakParsing;
-    final private int mVCardType;
-    final private Account mAccount;
+    private final boolean mStrictLineBreaking;
+    private final int mVCardType;
+    private final Account mAccount;
     
-    /** For measuring performance. */
+    // For measuring performance.
     private long mTimePushIntoContentResolver;
 
-    final private List<VCardEntryHandler> mEntryHandlers = new ArrayList<VCardEntryHandler>();
+    private final List<VCardEntryHandler> mEntryHandlers = new ArrayList<VCardEntryHandler>();
 
     public VCardEntryConstructor() {
-        this(null, null, false, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8, null);
+        this(VCardConfig.VCARD_TYPE_V21_GENERIC, null, null, false);
     }
 
     public VCardEntryConstructor(final int vcardType) {
-        this(null, null, false, vcardType, null);
+        this(vcardType, null, null, false);
     }
 
-    public VCardEntryConstructor(final String charset, final boolean strictLineBreakParsing,
-            final int vcardType, final Account account) {
-        this(null, charset, strictLineBreakParsing, vcardType, account);
+    public VCardEntryConstructor(final int vcardType, final Account account) {
+        this(vcardType, account, null, false);
     }
 
-    public VCardEntryConstructor(final String inputCharset, final String charsetForDetodedBytes,
-            final boolean strictLineBreakParsing, final int vcardType,
-            final Account account) {
+    public VCardEntryConstructor(final int vcardType, final Account account,
+            final String inputCharset) {
+        this(vcardType, account, inputCharset, false);
+    }
+
+    /**
+     * @hide
+     */
+    public VCardEntryConstructor(final int vcardType, final Account account,
+            final String inputCharset, final boolean strictLineBreakParsing) {
         if (inputCharset != null) {
-            mInputCharset = inputCharset;
+            mSourceCharset = inputCharset;
         } else {
-            mInputCharset = VCardConfig.DEFAULT_CHARSET;
+            mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
         }
-        if (charsetForDetodedBytes != null) {
-            mCharsetForDecodedBytes = charsetForDetodedBytes;
-        } else {
-            mCharsetForDecodedBytes = DEFAULT_CHARSET_FOR_DECODED_BYTES;
-        }
-        mStrictLineBreakParsing = strictLineBreakParsing;
+        mStrictLineBreaking = strictLineBreakParsing;
         mVCardType = vcardType;
         mAccount = account;
     }
@@ -108,30 +116,24 @@
         }
     }
 
-    /**
-     * Called when the parse failed between {@link #startEntry()} and {@link #endEntry()}.
-     */
     public void clear() {
-        mCurrentContactStruct = null;
+        mCurrentVCardEntry = null;
         mCurrentProperty = new VCardEntry.Property();
     }
 
-    /**
-     * Assume that VCard is not nested. In other words, this code does not accept 
-     */
     public void startEntry() {
-        if (mCurrentContactStruct != null) {
+        if (mCurrentVCardEntry != null) {
             Log.e(LOG_TAG, "Nested VCard code is not supported now.");
         }
-        mCurrentContactStruct = new VCardEntry(mVCardType, mAccount);
+        mCurrentVCardEntry = new VCardEntry(mVCardType, mAccount);
     }
 
     public void endEntry() {
-        mCurrentContactStruct.consolidateFields();
+        mCurrentVCardEntry.consolidateFields();
         for (VCardEntryHandler entryHandler : mEntryHandlers) {
-            entryHandler.onEntryCreated(mCurrentContactStruct);
+            entryHandler.onEntryCreated(mCurrentVCardEntry);
         }
-        mCurrentContactStruct = null;
+        mCurrentVCardEntry = null;
     }
 
     public void startProperty() {
@@ -139,7 +141,7 @@
     }
 
     public void endProperty() {
-        mCurrentContactStruct.addProperty(mCurrentProperty);
+        mCurrentVCardEntry.addProperty(mCurrentProperty);
     }
     
     public void propertyName(String name) {
@@ -166,113 +168,41 @@
         mParamType = null;
     }
 
-    private String encodeString(String originalString, String charsetForDecodedBytes) {
-        if (mInputCharset.equalsIgnoreCase(charsetForDecodedBytes)) {
+    private static String encodeToSystemCharset(String originalString,
+            String sourceCharset, String targetCharset) {
+        if (sourceCharset.equalsIgnoreCase(targetCharset)) {
             return originalString;
         }
-        Charset charset = Charset.forName(mInputCharset);
-        ByteBuffer byteBuffer = charset.encode(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.
-        byte[] bytes = new byte[byteBuffer.remaining()];
+        final byte[] bytes = new byte[byteBuffer.remaining()];
         byteBuffer.get(bytes);
         try {
-            return new String(bytes, charsetForDecodedBytes);
+            String ret = new String(bytes, targetCharset);
+            return ret;
         } catch (UnsupportedEncodingException e) {
-            Log.e(LOG_TAG, "Failed to encode: charset=" + charsetForDecodedBytes);
+            Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
             return null;
         }
     }
 
-    private String handleOneValue(String value, String charsetForDecodedBytes, String encoding) {
+    private String handleOneValue(String value,
+            String sourceCharset, String targetCharset, String encoding) {
         if (encoding != null) {
             if (encoding.equals("BASE64") || encoding.equals("B")) {
                 mCurrentProperty.setPropertyBytes(Base64.decodeBase64(value.getBytes()));
                 return value;
             } else if (encoding.equals("QUOTED-PRINTABLE")) {
-                // "= " -> " ", "=\t" -> "\t".
-                // Previous code had done this replacement. Keep on the safe side.
-                StringBuilder builder = new StringBuilder();
-                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);
-                }
-                String quotedPrintable = builder.toString();
-                
-                String[] lines;
-                if (mStrictLineBreakParsing) {
-                    lines = quotedPrintable.split("\r\n");
-                } else {
-                    builder = new StringBuilder();
-                    length = quotedPrintable.length();
-                    ArrayList<String> list = new ArrayList<String>();
-                    for (int i = 0; i < length; i++) {
-                        char ch = quotedPrintable.charAt(i);
-                        if (ch == '\n') {
-                            list.add(builder.toString());
-                            builder = new StringBuilder();
-                        } else if (ch == '\r') {
-                            list.add(builder.toString());
-                            builder = new StringBuilder();
-                            if (i < length - 1) {
-                                char nextCh = quotedPrintable.charAt(i + 1);
-                                if (nextCh == '\n') {
-                                    i++;
-                                }
-                            }
-                        } else {
-                            builder.append(ch);
-                        }
-                    }
-                    String finalLine = builder.toString();
-                    if (finalLine.length() > 0) {
-                        list.add(finalLine);
-                    }
-                    lines = list.toArray(new String[0]);
-                }
-                
-                builder = new StringBuilder();
-                for (String line : lines) {
-                    if (line.endsWith("=")) {
-                        line = line.substring(0, line.length() - 1);
-                    }
-                    builder.append(line);
-                }
-                byte[] bytes;
-                try {
-                    bytes = builder.toString().getBytes(mInputCharset);
-                } catch (UnsupportedEncodingException e1) {
-                    Log.e(LOG_TAG, "Failed to encode: charset=" + mInputCharset);
-                    bytes = builder.toString().getBytes();
-                }
-                
-                try {
-                    bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes);
-                } catch (DecoderException e) {
-                    Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e);
-                    return "";
-                }
-
-                try {
-                    return new String(bytes, charsetForDecodedBytes);
-                } catch (UnsupportedEncodingException e) {
-                    Log.e(LOG_TAG, "Failed to encode: charset=" + charsetForDecodedBytes);
-                    return new String(bytes);
-                }
+                return VCardUtils.parseQuotedPrintable(
+                        value, mStrictLineBreaking, sourceCharset, targetCharset);
             }
-            // Unknown encoding. Fall back to default.
+            Log.w(LOG_TAG, "Unknown encoding. Fall back to default.");
         }
-        return encodeString(value, charsetForDecodedBytes);
+
+        // Just translate the charset of a given String from inputCharset to a system one. 
+        return encodeToSystemCharset(value, sourceCharset, targetCharset);
     }
     
     public void propertyValues(List<String> values) {
@@ -281,23 +211,24 @@
         }
 
         final Collection<String> charsetCollection = mCurrentProperty.getParameters("CHARSET");
-        final String charset =
-            ((charsetCollection != null) ? charsetCollection.iterator().next() : null);
         final Collection<String> encodingCollection = mCurrentProperty.getParameters("ENCODING");
         final String encoding =
             ((encodingCollection != null) ? encodingCollection.iterator().next() : null);
-
-        String charsetForDecodedBytes = CharsetUtils.nameForDefaultVendor(charset);
-        if (charsetForDecodedBytes == null || charsetForDecodedBytes.length() == 0) {
-            charsetForDecodedBytes = mCharsetForDecodedBytes;
+        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, charsetForDecodedBytes, encoding));
+                    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/VCardEntryHandler.java b/core/java/android/pim/vcard/VCardEntryHandler.java
index 83a67fe..56bf69d 100644
--- a/core/java/android/pim/vcard/VCardEntryHandler.java
+++ b/core/java/android/pim/vcard/VCardEntryHandler.java
@@ -16,8 +16,13 @@
 package android.pim.vcard;
 
 /**
- * The interface called by {@link VCardEntryConstructor}. Useful when you don't want to
- * handle detailed information as what {@link VCardParser} provides via {@link VCardInterpreter}.
+ * <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 {
     /**
diff --git a/core/java/android/pim/vcard/VCardInterpreter.java b/core/java/android/pim/vcard/VCardInterpreter.java
index b5237c0..03704a2 100644
--- a/core/java/android/pim/vcard/VCardInterpreter.java
+++ b/core/java/android/pim/vcard/VCardInterpreter.java
@@ -20,7 +20,7 @@
 /**
  * <P>
  * The interface which should be implemented by the classes which have to analyze each
- * vCard entry more minutely than {@link VCardEntry} class analysis.
+ * vCard entry minutely.
  * </P>
  * <P>
  * Here, there are several terms specific to vCard (and this library).
diff --git a/core/java/android/pim/vcard/VCardInterpreterCollection.java b/core/java/android/pim/vcard/VCardInterpreterCollection.java
index 99f81f7..4952dc7 100644
--- a/core/java/android/pim/vcard/VCardInterpreterCollection.java
+++ b/core/java/android/pim/vcard/VCardInterpreterCollection.java
@@ -23,7 +23,7 @@
  * {@link VCardInterpreter} objects and make a user object treat them as one
  * {@link VCardInterpreter} object.
  */
-public class VCardInterpreterCollection implements VCardInterpreter {
+public final class VCardInterpreterCollection implements VCardInterpreter {
     private final Collection<VCardInterpreter> mInterpreterCollection;
     
     public VCardInterpreterCollection(Collection<VCardInterpreter> interpreterCollection) {
diff --git a/core/java/android/pim/vcard/VCardParser.java b/core/java/android/pim/vcard/VCardParser.java
index 57c52a6..31b9369 100644
--- a/core/java/android/pim/vcard/VCardParser.java
+++ b/core/java/android/pim/vcard/VCardParser.java
@@ -20,82 +20,36 @@
 import java.io.IOException;
 import java.io.InputStream;
 
-public abstract class VCardParser {
-    protected final int mParseType;
-    protected boolean mCanceled;
-
-    public VCardParser() {
-        this(VCardConfig.PARSE_TYPE_UNKNOWN);
-    }
-
-    public VCardParser(int parseType) {
-        mParseType = parseType;
-    }
-
+public interface VCardParser {
     /**
-     * <P>
-     * Parses the given stream and send the VCard data into VCardBuilderBase object.
-     * </P.
-     * <P>
+     * <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 recommended in VCard 3.0. In VCard 2.1,
-     * In some exreme case, some VCard may have different charsets in one VCard (though
-     * we do not see any device which emits such kind of malicious data)
-     * </P>
-     * <P>
-     * In order to avoid "misunderstanding" charset as much as possible, this method
-     * use "ISO-8859-1" for reading the stream. When charset is specified in some property
-     * (with "CHARSET=..." parameter), the string is decoded to raw bytes and encoded to
-     * the charset. This method assumes that "ISO-8859-1" has 1 to 1 mapping in all 8bit
-     * characters, which is not completely sure. In some cases, this "decoding-encoding"
-     * scheme may fail. To avoid the case,
-     * </P>
-     * <P>
-     * We recommend you to use {@link VCardSourceDetector} and detect which kind of source the
-     * VCard comes from and explicitly specify a charset using the result.
-     * </P>
+     * 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.
-     * @return Returns true for success. Otherwise returns false.
      * @throws IOException, VCardException
      */
-    public abstract boolean parse(InputStream is, VCardInterpreter interepreter)
+    public void parse(InputStream is, VCardInterpreter interepreter)
             throws IOException, VCardException;
-    
+
     /**
-     * <P>
-     * The method variants which accept charset.
-     * </P>
-     * <P>
-     * RFC 2426 "recommends" (not forces) to use UTF-8, so it may be OK to use
-     * UTF-8 as an encoding when parsing vCard 3.0. But note that some Japanese
-     * phone uses Shift_JIS as a charset (e.g. W61SH), and another uses
-     * "CHARSET=SHIFT_JIS", which is explicitly prohibited in vCard 3.0 specification (e.g. W53K).
-     * </P>
-     *
-     * @param is The source to parse.
-     * @param charset Charset to be used.
-     * @param builder The VCardBuilderBase object.
-     * @return Returns true when successful. Otherwise returns false.
-     * @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 boolean parse(InputStream is, String charset, VCardInterpreter builder)
-            throws IOException, VCardException;
-    
-    /**
-     * The method variants which tells this object the operation is already canceled.
-     */
-    public abstract void parse(InputStream is, String charset,
-            VCardInterpreter builder, boolean canceled)
-        throws IOException, VCardException;
-    
-    /**
-     * Cancel parsing.
-     * Actual cancel is done after the end of the current one vcard entry parsing.
-     */
-    public void cancel() {
-        mCanceled = true;
-    }
+    public abstract void cancel();
 }
diff --git a/core/java/android/pim/vcard/VCardParserImpl_V21.java b/core/java/android/pim/vcard/VCardParserImpl_V21.java
new file mode 100644
index 0000000..7d294cc
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardParserImpl_V21.java
@@ -0,0 +1,967 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.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 CustomBufferedReader extends BufferedReader {
+        private long mTime;
+
+        public CustomBufferedReader(Reader in) {
+            super(in);
+        }
+
+        @Override
+        public String readLine() throws IOException {
+            long start = System.currentTimeMillis();
+            String ret = super.readLine();
+            long end = System.currentTimeMillis();
+            mTime += end - start;
+            return ret;
+        }
+
+        public long getTotalmillisecond() {
+            return mTime;
+        }
+    }
+
+    private static final String sDefaultEncoding = "8BIT";
+
+    protected boolean mCanceled;
+    protected VCardInterpreter mInterpreter;
+
+    protected final String mImportCharset;
+
+    /**
+     * <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 BufferedReader 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, null);
+    }
+
+    public VCardParserImpl_V21(int vcardType) {
+        this(vcardType, null);
+    }
+
+    public VCardParserImpl_V21(int vcardType, String importCharset) {
+        if ((vcardType & VCardConfig.FLAG_TORELATE_NEST) != 0) {
+            mNestCount = 1;
+        }
+
+        mImportCharset = (!TextUtils.isEmpty(importCharset) ? importCharset :
+            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();
+    }
+
+    /**
+     * @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;
+        }
+        long start;
+        if (mInterpreter != null) {
+            start = System.currentTimeMillis();
+            mInterpreter.startEntry();
+            mTimeReadStartRecord += System.currentTimeMillis() - start;
+        }
+        start = System.currentTimeMillis();
+        parseItems();
+        mTimeParseItems += System.currentTimeMillis() - start;
+        readEndVCard(true, false);
+        if (mInterpreter != null) {
+            start = System.currentTimeMillis();
+            mInterpreter.endEntry();
+            mTimeReadEndRecord += System.currentTimeMillis() - start;
+        }
+        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;
+                }
+            }
+            String[] strArray = line.split(":", 2);
+            int length = strArray.length;
+
+            // Though vCard 2.1/3.0 specification does not allow lower cases,
+            // 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;
+
+        if (mInterpreter != null) {
+            long start = System.currentTimeMillis();
+            mInterpreter.startProperty();
+            mTimeStartProperty += System.currentTimeMillis() - start;
+        }
+        ended = parseItem();
+        if (mInterpreter != null && !ended) {
+            long start = System.currentTimeMillis();
+            mInterpreter.endProperty();
+            mTimeEndProperty += System.currentTimeMillis() - start;
+        }
+
+        while (!ended) {
+            // follow VCARD ,it wont reach endProperty
+            if (mInterpreter != null) {
+                long start = System.currentTimeMillis();
+                mInterpreter.startProperty();
+                mTimeStartProperty += System.currentTimeMillis() - start;
+            }
+            try {
+                ended = parseItem();
+            } catch (VCardInvalidCommentLineException e) {
+                Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored.");
+                ended = false;
+            }
+            if (mInterpreter != null && !ended) {
+                long start = System.currentTimeMillis();
+                mInterpreter.endProperty();
+                mTimeEndProperty += System.currentTimeMillis() - start;
+            }
+        }
+    }
+
+    /*
+     * 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 = sDefaultEncoding;
+
+        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;
+                        }
+                        if (mInterpreter != 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 if (mInterpreter != null) {
+                            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;
+                        }
+                        if (mInterpreter != null) {
+                            mInterpreter.propertyName(propertyName);
+                        }
+                        propertyNameAndValue[0] = propertyName;
+                        nameIndex = i + 1;
+                        state = STATE_PARAMS;  // Start parameter parsing.
+                    }
+                    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));
+        }
+        if (mInterpreter != null) {
+            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));
+        }
+        if (mInterpreter != null) {
+            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-")) {
+            if (mInterpreter != null) {
+                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) {
+        if (mInterpreter != null) {
+            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 + "\"");
+            }
+        }
+        if (mInterpreter != null) {
+            mInterpreter.propertyParamType("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) {
+        if (mInterpreter != null) {
+            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);
+            if (mInterpreter != null) {
+                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 String result = getBase64(propertyValue);
+                if (mInterpreter != null) {
+                    ArrayList<String> arrayList = new ArrayList<String>();
+                    arrayList.add(result);
+                    mInterpreter.propertyValues(arrayList);
+                }
+            } catch (OutOfMemoryError error) {
+                Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!");
+                if (mInterpreter != null) {
+                    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()));
+            }
+
+            final long start = System.currentTimeMillis();
+            if (mInterpreter != null) {
+                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);
+        }
+
+        if (mInterpreter != null) {
+            mInterpreter.propertyValues(VCardUtils.constructListFromValue(propertyValue,
+                    (getVersion() == VCardConfig.FLAG_V30)));
+        }
+    }
+
+    /*
+     * 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");
+        if (mReader instanceof CustomBufferedReader) {
+            Log.d(LOG_TAG, "Total readLine time: "
+                    + ((CustomBufferedReader) 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#FLAG_V21}
+     */
+    protected int getVersion() {
+        return VCardConfig.FLAG_V21;
+    }
+
+    /**
+     * @return {@link VCardConfig#FLAG_V30}
+     */
+    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 sDefaultEncoding;
+    }
+
+
+    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, mImportCharset);
+        if (VCardConfig.showPerformanceLog()) {
+            mReader = new CustomBufferedReader(tmpReader);
+        } else {
+            mReader = new BufferedReader(tmpReader);
+        }
+
+        mInterpreter = interpreter;
+
+        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
new file mode 100644
index 0000000..a48a3b4
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardParserImpl_V30.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.io.IOException;
+import java.util.Set;
+
+import android.pim.vcard.exception.VCardException;
+import android.util.Log;
+
+/**
+ * <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, null);
+    }
+
+    public VCardParserImpl_V30(int vcardType, String importCharset) {
+        super(vcardType, importCharset);
+    }
+
+    @Override
+    protected int getVersion() {
+        return VCardConfig.FLAG_V30;
+    }
+
+    @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) {
+        super.handleAnyParam(paramName, paramValue);
+    }
+
+    @Override
+    protected void handleParamWithoutName(final String paramValue) throws VCardException {
+        super.handleParamWithoutName(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
+     */
+    @Override
+    protected void handleType(final String ptypevalues) {
+        String[] ptypeArray = ptypevalues.split(",");
+        mInterpreter.propertyParamType("TYPE");
+        for (String value : ptypeArray) {
+            int length = value.length();
+            if (length >= 2 && value.startsWith("\"") && value.endsWith("\"")) {
+                mInterpreter.propertyParamValue(value.substring(1, value.length() - 1));
+            } else {
+                mInterpreter.propertyParamValue(value);
+            }
+        }
+    }
+
+    @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/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java
index fe8cfb0..b625695 100644
--- a/core/java/android/pim/vcard/VCardParser_V21.java
+++ b/core/java/android/pim/vcard/VCardParser_V21.java
@@ -15,922 +15,99 @@
  */
 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.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.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
 /**
- * This class is used to parse vCard. Please refer to vCard Specification 2.1 for more detail.
+ * </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 class VCardParser_V21 extends VCardParser {
-    private static final String LOG_TAG = "VCardParser_V21";
-
-    /** Store the known-type */
-    private static final HashSet<String> sKnownTypeSet = 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"));
-
-    /** Store the known-value */
-    private static final HashSet<String> sKnownValueSet = new HashSet<String>(
-            Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID"));
-
-    /** Store the property names available in vCard 2.1 */
-    private static final HashSet<String> sAvailablePropertyNameSetV21 =
-        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"));
+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...
+     * We allow it for safety.
+     * </p>
      */
-    private static final HashSet<String> sAvailableEncodingV21 =
-        new HashSet<String>(Arrays.asList(
-                "7BIT", "8BIT", "QUOTED-PRINTABLE", "BASE64", "B"));
-    
-    // Used only for parsing END:VCARD.
-    private String mPreviousLine;
-    
-    /** The builder to build parsed data */
-    protected VCardInterpreter mBuilder = null;
+    /* 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)));
 
-    /** 
-     * The encoding type. "Encoding" in vCard is different from "Charset".
-     * e.g. 7BIT, 8BIT, QUOTED-PRINTABLE. 
-     */
-    protected String mEncoding = null;
-    
-    protected final String sDefaultEncoding = "8BIT";
-    
-    // Should not directly read a line from this object. Use getLine() instead.
-    protected BufferedReader mReader;
-    
-    // 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.
-    private int mNestCount;
-    
-    // In order to reduce warning message as much as possible, we hold the value which made Logger
-    // emit a warning message.
-    protected Set<String> mUnknownTypeMap = new HashSet<String>();
-    protected Set<String> mUnknownValueMap = new HashSet<String>();
-
-    // 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;
+    private final VCardParserImpl_V21 mVCardParserImpl;
 
     public VCardParser_V21() {
-        this(null);
+        mVCardParserImpl = new VCardParserImpl_V21();
     }
 
-    public VCardParser_V21(VCardSourceDetector detector) {
-        this(detector != null ? detector.getEstimatedType() : VCardConfig.PARSE_TYPE_UNKNOWN);
+    public VCardParser_V21(int vcardType) {
+        mVCardParserImpl = new VCardParserImpl_V21(vcardType);
     }
 
-    public VCardParser_V21(int parseType) {
-        super(parseType);
-        if (parseType == VCardConfig.PARSE_TYPE_FOMA) {
-            mNestCount = 1;
-        }
+    public VCardParser_V21(int parseType, String inputCharset) {
+        mVCardParserImpl = new VCardParserImpl_V21(parseType, null);
     }
 
-    /**
-     * Parses the file at the given position.
-     *
-     * vcard_file = [wsls] vcard [wsls]
-     */
-    protected void parseVCardFile() throws IOException, VCardException {
-        boolean firstReading = true;
-        while (true) {
-            if (mCanceled) {
-                break;
-            }
-            if (!parseOneVCard(firstReading)) {
-                break;
-            }
-            firstReading = false;
-        }
-
-        if (mNestCount > 0) {
-            boolean useCache = true;
-            for (int i = 0; i < mNestCount; i++) {
-                readEndVCard(useCache, true);
-                useCache = false;
-            }
-        }
-    }
-
-    protected int getVersion() {
-        return VCardConfig.FLAG_V21;
-    }
-
-    protected String getVersionString() {
-        return VCardConstants.VERSION_V21;
-    }
-
-    /**
-     * @return true when the propertyName is a valid property name.
-     */
-    protected boolean isValidPropertyName(String propertyName) {
-        if (!(sAvailablePropertyNameSetV21.contains(propertyName.toUpperCase()) ||
-                propertyName.startsWith("X-")) && 
-                !mUnknownTypeMap.contains(propertyName)) {
-            mUnknownTypeMap.add(propertyName);
-            Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName);
-        }
-        return true;
-    }
-
-    /**
-     * @return true when the encoding is a valid encoding.
-     */
-    protected boolean isValidEncoding(String encoding) {
-        return sAvailableEncodingV21.contains(encoding.toUpperCase());
-    }
-    
-    /**
-     * @return String. It may be null, or its length may be 0
-     * @throws IOException
-     */
-    protected String getLine() throws IOException {
-        return mReader.readLine();
-    }
-    
-    /**
-     * @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 firstReading) throws IOException, VCardException {
-        boolean allowGarbage = false;
-        if (firstReading) {
-            if (mNestCount > 0) {
-                for (int i = 0; i < mNestCount; i++) {
-                    if (!readBeginVCard(allowGarbage)) {
-                        return false;
-                    }
-                    allowGarbage = true;
-                }
-            }
-        }
-
-        if (!readBeginVCard(allowGarbage)) {
-            return false;
-        }
-        long start;
-        if (mBuilder != null) {
-            start = System.currentTimeMillis();
-            mBuilder.startEntry();
-            mTimeReadStartRecord += System.currentTimeMillis() - start;
-        }
-        start = System.currentTimeMillis();
-        parseItems();
-        mTimeParseItems += System.currentTimeMillis() - start;
-        readEndVCard(true, false);
-        if (mBuilder != null) {
-            start = System.currentTimeMillis();
-            mBuilder.endEntry();
-            mTimeReadEndRecord += System.currentTimeMillis() - start;
-        }
-        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;
-                }
-            }
-            String[] strArray = line.split(":", 2);
-            int length = strArray.length;
-
-            // Though vCard 2.1/3.0 specification does not allow lower cases,
-            // 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.");
-    }
-
-    /**
-     * The arguments useCache and allowGarbase are usually true and false accordingly when
-     * this function is called outside this function itself. 
-     *
-     * @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)
+    public void parse(InputStream is, VCardInterpreter interepreter)
             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;
-        
-        if (mBuilder != null) {
-            long start = System.currentTimeMillis();
-            mBuilder.startProperty();
-            mTimeStartProperty += System.currentTimeMillis() - start;
-        }
-        ended = parseItem();
-        if (mBuilder != null && !ended) {
-            long start = System.currentTimeMillis();
-            mBuilder.endProperty();
-            mTimeEndProperty += System.currentTimeMillis() - start;
-        }
-
-        while (!ended) {
-            // follow VCARD ,it wont reach endProperty
-            if (mBuilder != null) {
-                long start = System.currentTimeMillis();
-                mBuilder.startProperty();
-                mTimeStartProperty += System.currentTimeMillis() - start;
-            }
-            try {
-                ended = parseItem();
-            } catch (VCardInvalidCommentLineException e) {
-                Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored.");
-                ended = false;
-            }
-            if (mBuilder != null && !ended) {
-                long start = System.currentTimeMillis();
-                mBuilder.endProperty();
-                mTimeEndProperty += System.currentTimeMillis() - start;
-            }
-        }
-    }
-    
-    /**
-     * 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 {
-        mEncoding = sDefaultEncoding;
-
-        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 + "\"");
+        mVCardParserImpl.parse(is, interepreter);
     }
 
-    static private final int STATE_GROUP_OR_PROPNAME = 0;
-    static private final int STATE_PARAMS = 1;
-    // vCard 3.0 specification allows double-quoted param-value, while vCard 2.1 does not.
-    // This is just for safety.
-    static private final int STATE_PARAMS_IN_DQUOTE = 2;
-
-    protected String[] separateLineAndHandleGroup(String line) throws VCardException {
-        int state = STATE_GROUP_OR_PROPNAME;
-        int nameIndex = 0;
-
-        final String[] propertyNameAndValue = new String[2];
-
-        final int length = line.length();
-        if (length > 0 && line.charAt(0) == '#') {
-            throw new VCardInvalidCommentLineException();
-        }
-
-        for (int i = 0; i < length; i++) {
-            char ch = line.charAt(i); 
-            switch (state) {
-                case STATE_GROUP_OR_PROPNAME: {
-                    if (ch == ':') {
-                        final String propertyName = line.substring(nameIndex, i);
-                        if (propertyName.equalsIgnoreCase("END")) {
-                            mPreviousLine = line;
-                            return null;
-                        }
-                        if (mBuilder != null) {
-                            mBuilder.propertyName(propertyName);
-                        }
-                        propertyNameAndValue[0] = propertyName;
-                        if (i < length - 1) {
-                            propertyNameAndValue[1] = line.substring(i + 1);
-                        } else {
-                            propertyNameAndValue[1] = "";
-                        }
-                        return propertyNameAndValue;
-                    } else if (ch == '.') {
-                        String groupName = line.substring(nameIndex, i);
-                        if (mBuilder != null) {
-                            mBuilder.propertyGroup(groupName);
-                        }
-                        nameIndex = i + 1;
-                    } else if (ch == ';') {
-                        String propertyName = line.substring(nameIndex, i);
-                        if (propertyName.equalsIgnoreCase("END")) {
-                            mPreviousLine = line;
-                            return null;
-                        }
-                        if (mBuilder != null) {
-                            mBuilder.propertyName(propertyName);
-                        }
-                        propertyNameAndValue[0] = propertyName;
-                        nameIndex = i + 1;
-                        state = STATE_PARAMS;
-                    }
-                    break;
-                }
-                case STATE_PARAMS: {
-                    if (ch == '"') {
-                        state = STATE_PARAMS_IN_DQUOTE;
-                    } else if (ch == ';') {
-                        handleParams(line.substring(nameIndex, i));
-                        nameIndex = i + 1;
-                    } else if (ch == ':') {
-                        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 == '"') {
-                        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 {
-        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 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) {
-        String upperTypeValue = ptypeval;
-        if (!(sKnownTypeSet.contains(upperTypeValue) || upperTypeValue.startsWith("X-")) && 
-                !mUnknownTypeMap.contains(ptypeval)) {
-            mUnknownTypeMap.add(ptypeval);
-            Log.w(LOG_TAG, "TYPE unsupported by vCard 2.1: " + ptypeval);
-        }
-        if (mBuilder != null) {
-            mBuilder.propertyParamType("TYPE");
-            mBuilder.propertyParamValue(upperTypeValue);
-        }
-    }
-    
-    /**
-     * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word
-     */
-    protected void handleValue(final String pvalueval) {
-        if (!sKnownValueSet.contains(pvalueval.toUpperCase()) &&
-                pvalueval.startsWith("X-") &&
-                !mUnknownValueMap.contains(pvalueval)) {
-            mUnknownValueMap.add(pvalueval);
-            Log.w(LOG_TAG, "VALUE unsupported by vCard 2.1: " + pvalueval);
-        }
-        if (mBuilder != null) {
-            mBuilder.propertyParamType("VALUE");
-            mBuilder.propertyParamValue(pvalueval);
-        }
-    }
-    
-    /**
-     * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word
-     */
-    protected void handleEncoding(String pencodingval) throws VCardException {
-        if (isValidEncoding(pencodingval) ||
-                pencodingval.startsWith("X-")) {
-            if (mBuilder != null) {
-                mBuilder.propertyParamType("ENCODING");
-                mBuilder.propertyParamValue(pencodingval);
-            }
-            mEncoding = pencodingval;
-        } else {
-            throw new VCardException("Unknown encoding \"" + pencodingval + "\"");
-        }
-    }
-    
-    /**
-     * vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC 1521),
-     * but today's vCard often contains other charset, so we allow them.
-     */
-    protected void handleCharset(String charsetval) {
-        if (mBuilder != null) {
-            mBuilder.propertyParamType("CHARSET");
-            mBuilder.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 (!isLetter(tmp.charAt(i))) {
-                throw new VCardException("Invalid Language: \"" + langval + "\"");
-            }
-        }
-        tmp = strArray[1];
-        length = tmp.length();
-        for (int i = 0; i < length; i++) {
-            if (!isLetter(tmp.charAt(i))) {
-                throw new VCardException("Invalid Language: \"" + langval + "\"");
-            }
-        }
-        if (mBuilder != null) {
-            mBuilder.propertyParamType("LANGUAGE");
-            mBuilder.propertyParamValue(langval);
-        }
-    }
-
-    /**
-     * Mainly for "X-" type. This accepts any kind of type without check.
-     */
-    protected void handleAnyParam(String paramName, String paramValue) {
-        if (mBuilder != null) {
-            mBuilder.propertyParamType(paramName);
-            mBuilder.propertyParamValue(paramValue);
-        }
-    }
-
-    protected void handlePropertyValue(String propertyName, String propertyValue)
-            throws IOException, VCardException {
-        if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
-            final long start = System.currentTimeMillis();
-            final String result = getQuotedPrintable(propertyValue);
-            if (mBuilder != null) {
-                ArrayList<String> v = new ArrayList<String>();
-                v.add(result);
-                mBuilder.propertyValues(v);
-            }
-            mTimeHandleQuotedPrintable += System.currentTimeMillis() - start;
-        } else if (mEncoding.equalsIgnoreCase("BASE64") ||
-                mEncoding.equalsIgnoreCase("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 String result = getBase64(propertyValue);
-                if (mBuilder != null) {
-                    ArrayList<String> v = new ArrayList<String>();
-                    v.add(result);
-                    mBuilder.propertyValues(v);
-                }
-            } catch (OutOfMemoryError error) {
-                Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!");
-                if (mBuilder != null) {
-                    mBuilder.propertyValues(null);
-                }
-            }
-            mTimeHandleBase64 += System.currentTimeMillis() - start;
-        } else {
-            if (!(mEncoding == null || mEncoding.equalsIgnoreCase("7BIT")
-                    || mEncoding.equalsIgnoreCase("8BIT")
-                    || mEncoding.toUpperCase().startsWith("X-"))) {
-                Log.w(LOG_TAG, "The encoding unsupported by vCard spec: \"" + mEncoding + "\".");
-            }
-
-            final long start = System.currentTimeMillis();
-            if (mBuilder != null) {
-                ArrayList<String> v = new ArrayList<String>();
-                v.add(maybeUnescapeText(propertyValue));
-                mBuilder.propertyValues(v);
-            }
-            mTimeHandleMiscPropertyValue += System.currentTimeMillis() - start;
-        }
-    }
-    
-    protected 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 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();
-    }
-    
-    /**
-     * Mainly for "ADR", "ORG", and "N"
-     * We do not care the number of strnosemi here.
-     * 
-     * 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 are not sure whether we should add "\" CRLF to each value.
-     * For now, we exclude them.               
-     */
-    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 (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
-            propertyValue = getQuotedPrintable(propertyValue);
-        }
-
-        if (mBuilder != null) {
-            mBuilder.propertyValues(VCardUtils.constructListFromValue(
-                    propertyValue, (getVersion() == VCardConfig.FLAG_V30)));
-        }
-    }
-
-    /**
-     * vCard 2.1 specifies AGENT allows one vcard entry. It is not encoded at all.
-     *
-     * 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.");
-        }
-        // TODO: Support AGENT property.
-    }
-    
-    /**
-     * 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);
-    }
-
-    public 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;
-        }
-    }
-    
-    @Override
-    public boolean parse(final InputStream is, final VCardInterpreter builder)
-            throws IOException, VCardException {
-        return parse(is, VCardConfig.DEFAULT_CHARSET, builder);
-    }
-    
-    @Override
-    public boolean parse(InputStream is, String charset, VCardInterpreter builder)
-            throws IOException, VCardException {
-        if (charset == null) {
-            charset = VCardConfig.DEFAULT_CHARSET;
-        }
-        final InputStreamReader tmpReader = new InputStreamReader(is, charset);
-        if (VCardConfig.showPerformanceLog()) {
-            mReader = new CustomBufferedReader(tmpReader);
-        } else {
-            mReader = new BufferedReader(tmpReader);
-        }
-        
-        mBuilder = builder;
-
-        long start = System.currentTimeMillis();
-        if (mBuilder != null) {
-            mBuilder.start();
-        }
-        parseVCardFile();
-        if (mBuilder != null) {
-            mBuilder.end();
-        }
-        mTimeTotal += System.currentTimeMillis() - start;
-        
-        if (VCardConfig.showPerformanceLog()) {
-            showPerformanceInfo();
-        }
-        
-        return true;
-    }
-    
-    @Override
-    public void parse(InputStream is, String charset, VCardInterpreter builder, boolean canceled)
-            throws IOException, VCardException {
-        mCanceled = canceled;
-        parse(is, charset, builder);
-    }
-        
-    private void showPerformanceInfo() {
-        Log.d(LOG_TAG, "Total parsing time:  " + mTimeTotal + " ms");
-        if (mReader instanceof CustomBufferedReader) {
-            Log.d(LOG_TAG, "Total readLine time: " +
-                    ((CustomBufferedReader)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");
-    }
-
-    private boolean isLetter(char ch) {
-        if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
-            return true;
-        }
-        return false;
-    }
-}
-
-class CustomBufferedReader extends BufferedReader {
-    private long mTime;
-    
-    public CustomBufferedReader(Reader in) {
-        super(in);
-    }
-    
-    @Override
-    public String readLine() throws IOException {
-        long start = System.currentTimeMillis();
-        String ret = super.readLine();
-        long end = System.currentTimeMillis();
-        mTime += end - start;
-        return ret;
-    }
-    
-    public long getTotalmillisecond() {
-        return mTime;
+    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
index 4ecfe97..40792ab 100644
--- a/core/java/android/pim/vcard/VCardParser_V30.java
+++ b/core/java/android/pim/vcard/VCardParser_V30.java
@@ -16,343 +16,76 @@
 package android.pim.vcard;
 
 import android.pim.vcard.exception.VCardException;
-import android.util.Log;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
+import java.util.Set;
 
 /**
- * The class used to parse vCard 3.0.
- * Please refer to vCard Specification 3.0 (http://tools.ietf.org/html/rfc2426).
+ * <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 extends VCardParser_V21 {
-    private static final String LOG_TAG = "VCardParser_V30";
-
-    private static final HashSet<String> sAcceptablePropsWithParam = new HashSet<String>(
-            Arrays.asList(
+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
-    
-    // Although "7bit" and "BASE64" is not allowed in vCard 3.0, we allow it for safety.
-    private static final HashSet<String> sAcceptableEncodingV30 = new HashSet<String>(
-            Arrays.asList("7BIT", "8BIT", "BASE64", "B"));
-    
-    // Although RFC 2426 specifies some property must not have parameters, we allow it, 
-    // since there may be some careers which violates the RFC...
-    private static final HashSet<String> acceptablePropsWithoutParam = new HashSet<String>();
-
-    private String mPreviousLine;
-
-    private boolean mEmittedAgentWarning = false;
+                    "SORT-STRING", "CATEGORIES", "PRODID"))); // 3.0
 
     /**
-     * True when the caller wants the parser to be strict about the input.
-     * Currently this is only for testing.
+     * <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>
      */
-    private final boolean mStrictParsing;
+    /* 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() {
-        super();
-        mStrictParsing = false;
+        mVCardParserImpl = new VCardParserImpl_V30();
     }
 
-    /**
-     * @param strictParsing when true, this object throws VCardException when the vcard is not
-     * valid from the view of vCard 3.0 specification (defined in RFC 2426). Note that this class
-     * is not fully yet for being used with this flag and may not notice invalid line(s).
-     *
-     * @hide currently only for testing! 
-     */
-    public VCardParser_V30(boolean strictParsing) {
-        super();
-        mStrictParsing = strictParsing;
+    public VCardParser_V30(int vcardType) {
+        mVCardParserImpl = new VCardParserImpl_V30(vcardType);
     }
 
-    public VCardParser_V30(int parseMode) {
-        super(parseMode);
-        mStrictParsing = false;
+    public VCardParser_V30(int vcardType, String importCharset) {
+        mVCardParserImpl = new VCardParserImpl_V30(vcardType, importCharset);
     }
 
-    @Override
-    protected int getVersion() {
-        return VCardConfig.FLAG_V30;
-    }
-
-    @Override
-    protected String getVersionString() {
-        return VCardConstants.VERSION_V30;
-    }
-
-    @Override
-    protected boolean isValidPropertyName(String propertyName) {
-        if (!(sAcceptablePropsWithParam.contains(propertyName) ||
-                acceptablePropsWithoutParam.contains(propertyName) ||
-                propertyName.startsWith("X-")) &&
-                !mUnknownTypeMap.contains(propertyName)) {
-            mUnknownTypeMap.add(propertyName);
-            Log.w(LOG_TAG, "Property name unsupported by vCard 3.0: " + propertyName);
-        }
-        return true;
-    }
-
-    @Override
-    protected boolean isValidEncoding(String encoding) {
-        return sAcceptableEncodingV30.contains(encoding.toUpperCase());
-    }
-
-    @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)
+    public void parse(InputStream is, VCardInterpreter interepreter)
             throws IOException, VCardException {
-        // TODO: vCard 3.0 supports group.
-        super.readEndVCard(useCache, allowGarbage);
+        mVCardParserImpl.parse(is, interepreter);
     }
 
-    /**
-     * vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not.
-     */
-    @Override
-    protected void handleParams(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(String paramName, String paramValue) {
-        super.handleAnyParam(paramName, paramValue);
-    }
-
-    @Override
-    protected void handleParamWithoutName(final String paramValue) throws VCardException {
-        if (mStrictParsing) {
-            throw new VCardException("Parameter without name is not acceptable in vCard 3.0");
-        } else {
-            super.handleParamWithoutName(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
-     */
-    @Override
-    protected void handleType(String ptypevalues) {
-        String[] ptypeArray = ptypevalues.split(",");
-        mBuilder.propertyParamType("TYPE");
-        for (String value : ptypeArray) {
-            int length = value.length();
-            if (length >= 2 && value.startsWith("\"") && value.endsWith("\"")) {
-                mBuilder.propertyParamValue(value.substring(1, value.length() - 1));
-            } else {
-                mBuilder.propertyParamValue(value);
-            }
-        }
-    }
-
-    @Override
-    protected void handleAgent(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(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;
-            } 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(String text) {
-        return unescapeText(text);
-    }
-
-    public static String unescapeText(String text) {
-        StringBuilder builder = new StringBuilder();
-        int length = text.length();
-        for (int i = 0; i < length; i++) {
-            char ch = text.charAt(i);
-            if (ch == '\\' && i < length - 1) {
-                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(char ch) {
-        return unescapeCharacter(ch);
-    }
-
-    public static String unescapeCharacter(char ch) {
-        if (ch == 'n' || ch == 'N') {
-            return "\n";
-        } else {
-            return String.valueOf(ch);
-        }        
+    public void cancel() {
+        mVCardParserImpl.cancel();
     }
 }
diff --git a/core/java/android/pim/vcard/VCardSourceDetector.java b/core/java/android/pim/vcard/VCardSourceDetector.java
index 7297c50..291deca 100644
--- a/core/java/android/pim/vcard/VCardSourceDetector.java
+++ b/core/java/android/pim/vcard/VCardSourceDetector.java
@@ -15,15 +15,28 @@
  */
 package android.pim.vcard;
 
+import android.text.TextUtils;
+
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
 /**
- * Class which tries to detects the source of the vCard from its properties.
- * Currently this implementation is very premature.
- * @hide
+ * <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 Set<String> APPLE_SIGNS = new HashSet<String>(Arrays.asList(
@@ -42,8 +55,22 @@
             "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";
-    
-    private int mType = VCardConfig.PARSE_TYPE_UNKNOWN;
+
+
+    // TODO: Should replace this with types in VCardConfig
+    private 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.
+
     // Some mobile phones (like FOMA) tells us the charset of the data.
     private boolean mNeedParseSpecifiedCharset;
     private String mSpecifiedCharset;
@@ -72,21 +99,22 @@
     
     public void propertyName(String name) {
         if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) {
-            mType = VCardConfig.PARSE_TYPE_FOMA;
+            mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST;
+            // Probably Shift_JIS is used, but we should double confirm.
             mNeedParseSpecifiedCharset = true;
             return;
         }
-        if (mType != VCardConfig.PARSE_TYPE_UNKNOWN) {
+        if (mParseType != PARSE_TYPE_UNKNOWN) {
             return;
         }
         if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) {
-            mType = VCardConfig.PARSE_TYPE_WINDOWS_MOBILE_JP;
+            mParseType = PARSE_TYPE_WINDOWS_MOBILE_V65_JP;
         } else if (FOMA_SIGNS.contains(name)) {
-            mType = VCardConfig.PARSE_TYPE_FOMA;
+            mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST;
         } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) {
-            mType = VCardConfig.PARSE_TYPE_MOBILE_PHONE_JP;
+            mParseType = PARSE_TYPE_MOBILE_PHONE_JP;
         } else if (APPLE_SIGNS.contains(name)) {
-            mType = VCardConfig.PARSE_TYPE_APPLE;
+            mParseType = PARSE_TYPE_APPLE;
         }
     }
 
@@ -102,25 +130,40 @@
         }
     }
 
-    /* package */ int getEstimatedType() {
-        return mType;
-    }
-    
     /**
-     * Return charset String guessed from the source's properties.
+     * @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:
+                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 (mSpecifiedCharset != null) {
+        if (TextUtils.isEmpty(mSpecifiedCharset)) {
             return mSpecifiedCharset;
         }
-        switch (mType) {
-            case VCardConfig.PARSE_TYPE_WINDOWS_MOBILE_JP:
-            case VCardConfig.PARSE_TYPE_FOMA:
-            case VCardConfig.PARSE_TYPE_MOBILE_PHONE_JP:
+        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 VCardConfig.PARSE_TYPE_APPLE:
+            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
index 11b112b..680ef6f 100644
--- a/core/java/android/pim/vcard/VCardUtils.java
+++ b/core/java/android/pim/vcard/VCardUtils.java
@@ -22,7 +22,12 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 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.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -34,8 +39,11 @@
 
 /**
  * Utilities for VCard handling codes.
+ * @hide
  */
 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. 
@@ -240,10 +248,13 @@
     }
 
     /**
+     * <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,
@@ -329,8 +340,8 @@
             if (ch == '\\' && i < length - 1) {
                 char nextCh = value.charAt(i + 1);
                 final String unescapedString =
-                    (isV30 ? VCardParser_V30.unescapeCharacter(nextCh) :
-                        VCardParser_V21.unescapeCharacter(nextCh));
+                    (isV30 ? VCardParserImpl_V30.unescapeCharacter(nextCh) :
+                        VCardParserImpl_V21.unescapeCharacter(nextCh));
                 if (unescapedString != null) {
                     builder.append(unescapedString);
                     i++;
@@ -371,9 +382,13 @@
     }
 
     /**
+     * <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) {
@@ -407,13 +422,16 @@
         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. 
+     *       to the device which is able to parse the malformed input.
+     * </p>
      */
     public static boolean containsOnlyAlphaDigitHyphen(final String...values) {
         if (values == null) {
@@ -452,13 +470,13 @@
     }
 
     /**
-     * <P>
+     * <p>
      * Returns true when the given String is categorized as "word" specified in vCard spec 2.1.
-     * </P>
-     * <P>
-     * vCard 2.1 specifies:<BR />
+     * </p>
+     * <p>
+     * vCard 2.1 specifies:<br />
      * word = &lt;any printable 7bit us-ascii except []=:., &gt;
-     * </P>
+     * </p>
      */
     public static boolean isV21Word(final String value) {
         if (TextUtils.isEmpty(value)) {
@@ -540,6 +558,96 @@
         return true;
     }
 
+    //// The methods bellow may be used by unit test.
+
+    /**
+     * @hide 
+     */
+    public static String parseQuotedPrintable(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);
+        }
+        byte[] bytes;
+        try {
+            bytes = builder.toString().getBytes(sourceCharset);
+        } catch (UnsupportedEncodingException e1) {
+            Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
+            bytes = builder.toString().getBytes();
+        }
+
+        try {
+            bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes);
+        } catch (DecoderException e) {
+            Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e);
+            return "";
+        }
+
+        try {
+            return new String(bytes, targetCharset);
+        } catch (UnsupportedEncodingException e) {
+            Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
+            return new String(bytes);
+        }
+    }
+
     private VCardUtils() {
     }
 }
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 197d976..381f794 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
@@ -1250,6 +1252,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..4686978 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -23,7 +23,10 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.text.TextUtils;
 import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
 
 /**
  * Shows a hierarchy of {@link Preference} objects as
@@ -69,30 +72,43 @@
  * 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
  */
 public abstract class PreferenceActivity extends ListActivity implements
         PreferenceManager.OnPreferenceTreeClickListener {
-    
+
     private static final String PREFERENCES_TAG = "android:preferences";
-    
+
+    // 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";
+
+    // 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";
+
+    private Button mNextButton;
+
     private PreferenceManager mPreferenceManager;
-    
+
     private Bundle mSavedInstanceState;
 
     /**
      * 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 Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                
+
                 case MSG_BIND_PREFERENCES:
                     bindPreferences();
                     break;
@@ -105,7 +121,49 @@
         super.onCreate(savedInstanceState);
 
         setContentView(com.android.internal.R.layout.preference_list_content);
-        
+
+        // 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();
+                }
+            });
+            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);
+                }
+            }
+        }
+
         mPreferenceManager = onCreatePreferenceManager();
         getListView().setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
     }
@@ -113,14 +171,13 @@
     @Override
     protected void onStop() {
         super.onStop();
-        
+
         mPreferenceManager.dispatchActivityStop();
     }
 
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        
         mPreferenceManager.dispatchActivityDestroy();
     }
 
@@ -156,7 +213,7 @@
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
-        
+
         mPreferenceManager.dispatchActivityResult(requestCode, resultCode, data);
     }
 
@@ -176,7 +233,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,10 +244,10 @@
             }
         }
     }
-    
+
     /**
      * Creates the {@link PreferenceManager}.
-     * 
+     *
      * @return The {@link PreferenceManager} used by this activity.
      */
     private PreferenceManager onCreatePreferenceManager() {
@@ -198,7 +255,7 @@
         preferenceManager.setOnPreferenceTreeClickListener(this);
         return preferenceManager;
     }
-    
+
     /**
      * Returns the {@link PreferenceManager} used by this activity.
      * @return The {@link PreferenceManager}.
@@ -206,7 +263,7 @@
     public PreferenceManager getPreferenceManager() {
         return mPreferenceManager;
     }
-    
+
     private void requirePreferenceManager() {
         if (mPreferenceManager == null) {
             throw new RuntimeException("This should be called after super.onCreate.");
@@ -215,7 +272,7 @@
 
     /**
      * Sets the root of the preference hierarchy that this activity is showing.
-     * 
+     *
      * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
      */
     public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
@@ -228,37 +285,37 @@
             }
         }
     }
-    
+
     /**
      * Gets the root of the preference hierarchy that this activity 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(this, preferencesResId,
                 getPreferenceScreen()));
     }
@@ -269,20 +326,20 @@
     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)
      */
     public Preference findPreference(CharSequence key) {
-        
+
         if (mPreferenceManager == null) {
             return null;
         }
-        
+
         return mPreferenceManager.findPreference(key);
     }
 
@@ -292,5 +349,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/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 40a408a..abeb931 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -4902,6 +4902,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";
     }
 
     /**
@@ -5042,6 +5059,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);
             }
@@ -5558,6 +5577,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.
@@ -5578,6 +5619,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.
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 893db2e..ac89934 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -554,6 +554,7 @@
         if (!result) {
             if (deviceObjectPath != null) {
                 String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+                if (address == null) return;
                 BluetoothDevice device = mAdapter.getRemoteDevice(address);
                 int state = getSinkState(device);
                 handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTED);
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/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/Layout.java b/core/java/android/text/Layout.java
index 38ac9b7..3b8f295 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -16,25 +16,29 @@
 
 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.view.KeyEvent;
 
+import junit.framework.Assert;
+
 /**
- * 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 {
@@ -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,8 @@
                 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, true, null);
 
             if (w > need)
                 need = w;
@@ -116,6 +118,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();
@@ -185,13 +196,13 @@
         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;
@@ -238,7 +249,7 @@
             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 +266,8 @@
         }
 
         Alignment align = mAlignment;
-        
+
+        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.
@@ -271,7 +283,7 @@
             int lbaseline = lbottom - getLineDescent(i);
 
             boolean isFirstParaLine = false;
-            if (spannedText) { 
+            if (spannedText) {
                 if (start == 0 || buf.charAt(start - 1) == '\n') {
                     isFirstParaLine = true;
                 }
@@ -282,7 +294,7 @@
                     spanend = sp.nextSpanTransition(start, textLength,
                                                     ParagraphStyle.class);
                     spans = sp.getSpans(start, spanend, ParagraphStyle.class);
-                    
+
                     align = mAlignment;
                     for (int n = spans.length-1; n >= 0; n--) {
                         if (spans[n] instanceof AlignmentSpan) {
@@ -292,7 +304,7 @@
                     }
                 }
             }
-            
+
             int dir = getParagraphDirection(i);
             int left = 0;
             int right = mWidth;
@@ -309,7 +321,7 @@
                             margin.drawLeadingMargin(c, paint, right, dir, ltop,
                                                      lbaseline, lbottom, buf,
                                                      start, end, isFirstParaLine, this);
-                                
+
                             right -= margin.getLeadingMargin(isFirstParaLine);
                         } else {
                             margin.drawLeadingMargin(c, paint, left, dir, ltop,
@@ -367,11 +379,11 @@
                 // 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, hasTab, spans);
+                tl.draw(c, x, ltop, lbaseline, lbottom);
             }
         }
+        TextLine.recycle(tl);
     }
 
     /**
@@ -417,7 +429,7 @@
 
         mWidth = wid;
     }
-    
+
     /**
      * Return the total height of this layout.
      */
@@ -450,7 +462,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 +536,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,19 +633,19 @@
      * 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);
         Directions directions = getLineDirections(line);
@@ -561,17 +655,10 @@
             tabs = ((Spanned) mText).getSpans(start, end, TabStopSpan.class);
         }
 
-        float wid = measureText(mPaint, mWorkPaint, mText, start, offset, end,
-                                dir, directions, trailing, alt, tab, tabs);
-
-        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);
-        }
+        TextLine tl = TextLine.obtain();
+        tl.set(mPaint, mText, start, end, dir, directions, tab, tabs);
+        float wid = tl.measure(offset - start, trailing, null);
+        TextLine.recycle(tl);
 
         Alignment align = getParagraphAlignment(line);
         int left = getParagraphLeft(line);
@@ -673,21 +760,15 @@
 
     private float getLineMax(int line, Object[] tabs, boolean full) {
         int start = getLineStart(line);
-        int end;
+        int end = full ? getLineEnd(line) : getLineVisibleEnd(line);
+        boolean hasTabs = getLineContainsTab(line);
+        Directions directions = getLineDirections(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);
-        }
-
-        return measureText(mPaint, mWorkPaint,
-                           mText, start, end, null, tab, tabs);
+        TextLine tl = TextLine.obtain();
+        tl.set(mPaint, mText, start, end, 1, directions, hasTabs, tabs);
+        float width = tl.metrics(null);
+        TextLine.recycle(tl);
+        return width;
     }
 
     /**
@@ -738,7 +819,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) {
@@ -752,14 +833,13 @@
         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) {
@@ -792,7 +872,7 @@
 
                 if (dist < bestdist) {
                     bestdist = dist;
-                    best = low;   
+                    best = low;
                 }
             }
 
@@ -802,8 +882,6 @@
                 bestdist = dist;
                 best = here;
             }
-
-            here = there;
         }
 
         float dist = Math.abs(getPrimaryHorizontal(max) - horiz);
@@ -823,14 +901,14 @@
         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);
@@ -882,207 +960,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--;
-
-        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--;
+    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;
 
@@ -1115,7 +1048,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,
@@ -1127,7 +1060,8 @@
         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) |
@@ -1204,9 +1138,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;
 
@@ -1215,14 +1150,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;
         }
     }
 
@@ -1257,7 +1190,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);
@@ -1371,361 +1304,28 @@
         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;
+    /* package */
+    static float measurePara(TextPaint paint, TextPaint workPaint,
+            CharSequence text, int start, int end, boolean hasTabs,
+            Object[] tabs) {
+
+        MeasuredText mt = MeasuredText.obtain();
+        TextLine tl = TextLine.obtain();
+        try {
+            mt.setPara(text, start, end, DIR_REQUEST_LTR);
+            Directions directions;
+            if (mt.mEasy){
+                directions = DIRS_ALL_LEFT_TO_RIGHT;
+            } else {
+                directions = AndroidBidi.directions(mt.mDir, mt.mLevels,
+                    0, mt.mChars, 0, mt.mLen);
             }
-            buf = null;
-        } else {
-            buf = TextUtils.obtain(end - start);
-            TextUtils.getChars(text, start, end, buf, 0);
+            tl.set(paint, text, start, end, 1, directions, hasTabs, tabs);
+            return tl.metrics(null);
+        } finally {
+            TextLine.recycle(tl);
+            MeasuredText.recycle(mt);
         }
-
-        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 (bm != null) {
-                        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;
-    }
-
-    /**
-     * 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
-     */
-    /* 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);
-        }
-
-        int len = end - start;
-
-        int lastPos = 0;
-        float width = 0;
-        int ascent = 0, descent = 0, top = 0, bottom = 0;
-
-        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];
-            }
-
-            if (codept >= 0xD800 && codept <= 0xDFFF && pos < len) {
-                codept = Character.codePointAt(buf, pos);
-
-                if (codept >= MIN_EMOJI && codept <= MAX_EMOJI) {
-                    bm = EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
-                }
-            }
-
-            if (pos == len || codept == '\t' || bm != null) {
-                workPaint.baselineShift = 0;
-
-                width += Styled.measureText(paint, workPaint, text,
-                                        start + lastPos, start + pos,
-                                        fm);
-
-                if (fm != null) {
-                    if (workPaint.baselineShift < 0) {
-                        fm.ascent += workPaint.baselineShift;
-                        fm.top += workPaint.baselineShift;
-                    } else {
-                        fm.descent += workPaint.baselineShift;
-                        fm.bottom += workPaint.baselineShift;
-                    }
-                }
-
-                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 (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.
-                }
-
-                lastPos = pos + 1;
-            }
-        }
-
-        if (fm != null) {
-            fm.ascent = ascent;
-            fm.descent = descent;
-            fm.top = top;
-            fm.bottom = bottom;
-        }
-
-        if (hasTabs)
-            TextUtils.recycle(buf);
-
-        return width;
     }
 
     /**
@@ -1804,23 +1404,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;
         }
     }
@@ -1831,6 +1430,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.
@@ -1870,7 +1470,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);
@@ -1936,12 +1536,17 @@
 
     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,
@@ -1953,9 +1558,8 @@
     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..e3a113d
--- /dev/null
+++ b/core/java/android/text/MeasuredText.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 android.text;
+
+import com.android.internal.util.ArrayUtils;
+
+import android.graphics.Paint;
+import android.icu.text.ArabicShaping;
+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 float[] mWorkWidths; // temp buffer for Paint.measureText, arrgh
+    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)];
+            mWorkWidths = new float[mWidths.length];
+        }
+        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 = 1;
+            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;
+
+            // shape
+            if (mLen > 0) {
+                byte[] levels = mLevels;
+                char[] chars = mChars;
+                byte level = levels[0];
+                int pi = 0;
+                for (int i = 1, e = mLen;; ++i) {
+                    if (i == e || levels[i] != level) {
+                        if ((level & 0x1) != 0) {
+                            AndroidCharacter.mirror(chars, pi, i - pi);
+                            ArabicShaping.SHAPER.shape(chars, pi, i - pi);
+                        }
+                        if (i == e) {
+                            break;
+                        }
+                        pi = i;
+                        level = levels[i];
+                    }
+                }
+            }
+        }
+    }
+
+    float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) {
+        int p = mPos;
+        float[] w = mWidths, ww = mWorkWidths;
+        int count = paint.getTextWidths(mChars, p, len, ww);
+        int width = 0;
+        if (count < len) {
+            // must have surrogate pairs in here, pad out the array with zero
+            // for the trailing surrogates
+            char[] chars = mChars;
+            for (int i = 0, e = mLen; i < count; ++i) {
+                width += (w[p++] = ww[i]);
+                if (p < e && chars[p] >= '\udc00' && chars[p] < '\ue000' &&
+                        chars[p-1] >= '\ud800' && chars[p-1] < '\udc00') {
+                    w[p++] = 0;
+                }
+            }
+        } else {
+            for (int i = 0; i < len; ++i) {
+                width += (w[p++] = ww[i]);
+            }
+        }
+        mPos = p;
+        if (fm != null) {
+            paint.getFontMetricsInt(fm);
+        }
+        return width;
+    }
+
+    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/StaticLayout.java b/core/java/android/text/StaticLayout.java
index f02ad2a..0c6c545 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -16,14 +16,13 @@
 
 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;
 
 /**
  * StaticLayout is a Layout for text that will not be edited after it
@@ -31,8 +30,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 +62,7 @@
                         boolean includepad,
                         TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
         super((ellipsize == null)
-                ? source 
+                ? source
                 : (source instanceof Spanned)
                     ? new SpannedEllipsizer(source)
                     : new Ellipsizer(source),
@@ -72,7 +72,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 +94,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 +111,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,38 +129,22 @@
         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);
-
-            if (end < 0)
-                end = bufend;
-            else
-                end++;
+                paraEnd++;
+            int paraLen = paraEnd - paraStart;
 
             int firstWidthLineCount = 1;
             int firstwidth = outerwidth;
@@ -168,19 +153,20 @@
             LineHeightSpan[] chooseht = null;
 
             if (spanned != null) {
-                LeadingMarginSpan[] sp;
-
-                sp = spanned.getSpans(start, end, LeadingMarginSpan.class);
+                LeadingMarginSpan[] sp = spanned.getSpans(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();
+                        firstWidthLineCount =
+                            ((LeadingMarginSpan.LeadingMarginSpan2)lms)
+                            .getLeadingMarginLineCount();
                     }
                 }
 
-                chooseht = spanned.getSpans(start, end, LineHeightSpan.class);
+                chooseht = spanned.getSpans(paraStart, paraEnd, LineHeightSpan.class);
 
                 if (chooseht.length != 0) {
                     if (choosehtv == null ||
@@ -192,11 +178,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,134 +192,48 @@
                 }
             }
 
-            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;
 
-            int next;
-            for (int i = start; i < end; i = next) {
+            int spanEnd;
+            for (int spanStart = paraStart; spanStart < paraEnd; spanStart = spanEnd) {
                 if (spanned == null)
-                    next = end;
+                    spanEnd = paraEnd;
                 else
-                    next = spanned.nextSpanTransition(i, end,
-                                                      MetricAffectingSpan.
-                                                      class);
+                    spanEnd = spanned.nextSpanTransition(spanStart, paraEnd,
+                            MetricAffectingSpan.class);
+
+                int spanLen = spanEnd - spanStart;
+                int startInPara = spanStart - paraStart;
+                int endInPara = spanEnd - paraStart;
 
                 if (spanned == null) {
-                    paint.getTextWidths(sub, i, next, widths);
-                    System.arraycopy(widths, 0, widths,
-                                     end - start + (i - start), next - i);
-                                     
-                    paint.getFontMetricsInt(fm);
+                    measured.addStyleRun(paint, spanLen, fm);
                 } else {
-                    mWorkPaint.baselineShift = 0;
-
-                    Styled.getTextWidths(paint, mWorkPaint,
-                                         spanned, i, next,
-                                         widths, fm);
-                    System.arraycopy(widths, 0, widths,
-                                     end - start + (i - start), next - i);
-
-                    if (mWorkPaint.baselineShift < 0) {
-                        fm.ascent += mWorkPaint.baselineShift;
-                        fm.top += mWorkPaint.baselineShift;
-                    } else {
-                        fm.descent += mWorkPaint.baselineShift;
-                        fm.bottom += mWorkPaint.baselineShift;
-                    }
+                    MetricAffectingSpan[] spans =
+                        spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
+                    measured.addStyleRun(paint, spans, spanLen, fm);
                 }
 
                 int fmtop = fm.top;
@@ -341,27 +241,17 @@
                 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);
+                        w = Layout.nextTab(sub, paraStart, paraEnd, w, null);
                         tab = true;
-                    } else if (c >= 0xD800 && c <= 0xDFFF && j + 1 < next) {
-                        int emoji = Character.codePointAt(chs, j - start);
+                    } 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.
@@ -376,7 +266,7 @@
                                     whichPaint = mWorkPaint;
                                 }
 
-                                float wid = (float) bm.getWidth() *
+                                float wid = bm.getWidth() *
                                             -whichPaint.ascent() /
                                             bm.getHeight();
 
@@ -384,13 +274,13 @@
                                 tab = 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);
@@ -411,7 +301,7 @@
                         /*
                          * From the Unicode Line Breaking Algorithm:
                          * (at least approximately)
-                         *  
+                         *
                          * .,:; are class IS: breakpoints
                          *      except when adjacent to digits
                          * /    is class SY: a breakpoint
@@ -426,12 +316,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;
 
@@ -448,7 +338,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++;
                             }
 
@@ -458,9 +348,9 @@
                                     v,
                                     spacingmult, spacingadd, chooseht,
                                     choosehtv, fm, tab,
-                                    needMultiply, start, chdirs, dir, easy,
+                                    needMultiply, paraStart, chdirs, dir, easy,
                                     ok == bufend, includepad, trackpad,
-                                    widths, start, end - start,
+                                    chs, widths, here - paraStart,
                                     where, ellipsizedWidth, okwidth,
                                     paint);
 
@@ -484,7 +374,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++;
                             }
 
@@ -494,9 +384,9 @@
                                     v,
                                     spacingmult, spacingadd, chooseht,
                                     choosehtv, fm, tab,
-                                    needMultiply, start, chdirs, dir, easy,
+                                    needMultiply, paraStart, chdirs, dir, easy,
                                     ok == bufend, includepad, trackpad,
-                                    widths, start, end - start,
+                                    chs, widths, here - paraStart,
                                     where, ellipsizedWidth, okwidth,
                                     paint);
 
@@ -510,18 +400,19 @@
                                     v,
                                     spacingmult, spacingadd, chooseht,
                                     choosehtv, fm, tab,
-                                    needMultiply, start, chdirs, dir, easy,
+                                    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,
@@ -530,18 +421,18 @@
                                     v,
                                     spacingmult, spacingadd, chooseht,
                                     choosehtv, fm, tab,
-                                    needMultiply, start, chdirs, dir, easy,
+                                    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) {
+                            j = spanEnd = here; // must remeasure
                         } else {
                             j = here - 1;    // continue looping
                         }
@@ -558,7 +449,7 @@
                 }
             }
 
-            if (end != here) {
+            if (paraEnd != here) {
                 if ((fittop | fitbottom | fitdescent | fitascent) == 0) {
                     paint.getFontMetricsInt(fm);
 
@@ -571,20 +462,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,
+                        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;
         }
 
@@ -599,246 +490,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
@@ -944,28 +602,6 @@
     }
 */
 
-    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 int out(CharSequence text, int start, int end,
                       int above, int below, int top, int bottom, int v,
                       float spacingmult, float spacingadd,
@@ -974,7 +610,7 @@
                       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;
@@ -982,8 +618,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];
@@ -1062,56 +696,20 @@
         if (tab)
             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);
             }
@@ -1122,7 +720,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;
@@ -1142,7 +740,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;
@@ -1158,7 +756,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;
@@ -1175,7 +773,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;
@@ -1186,7 +784,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;
@@ -1203,7 +801,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.
@@ -1232,11 +830,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) {
@@ -1312,10 +910,8 @@
     private static final char FIRST_RIGHT_TO_LEFT = '\u0590';
 
     /*
-     * 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 100644
index 513b2cd..0000000
--- a/core/java/android/text/Styled.java
+++ /dev/null
@@ -1,434 +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);
-			}
-		}
-	
-        if (replacement == null) {
-            workPaint.getFontMetricsInt(fmi);
-            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;
-            }
-        }
-        return end - start;
-    }
-
-    /**
-     * 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..8ab481b
--- /dev/null
+++ b/core/java/android/text/TextLine.java
@@ -0,0 +1,1053 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.RectF;
+import android.graphics.Paint.FontMetricsInt;
+import android.icu.text.ArabicShaping;
+import android.text.Layout.Directions;
+import android.text.style.CharacterStyle;
+import android.text.style.MetricAffectingSpan;
+import android.text.style.ReplacementSpan;
+import android.text.style.TabStopSpan;
+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 TabStopSpan[] mTabs;
+
+    private char[] mChars;
+    private boolean mCharsValid;
+    private Spanned mSpanned;
+    private TextPaint mWorkPaint = new TextPaint();
+    private int mPreppedIndex;
+    private int mPreppedLimit;
+
+    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.e("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 spans array of paragraph-level spans, of which only TabStopSpans
+     * are used.  Can be null.
+     */
+    void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
+            Directions directions, boolean hasTabs, Object[] spans) {
+        mPaint = paint;
+        mText = text;
+        mStart = start;
+        mLen = limit - start;
+        mDir = dir;
+        mDirections = directions;
+        mHasTabs = hasTabs;
+        mSpanned = null;
+        mPreppedIndex = 0;
+        mPreppedLimit = 0;
+
+        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 (hasTabs) {
+                TabStopSpan[] tabs = mTabs;
+                int tabLen = 0;
+                if (mSpanned != null && spans == null) {
+                    TabStopSpan[] newTabs = mSpanned.getSpans(start, limit,
+                            TabStopSpan.class);
+                    if (tabs == null || tabs.length < newTabs.length) {
+                        tabs = newTabs;
+                    } else {
+                        for (int i = 0; i < newTabs.length; ++i) {
+                            tabs[i] = newTabs[i];
+                        }
+                    }
+                    tabLen = newTabs.length;
+                } else if (spans != null) {
+                    if (tabs == null || tabs.length < spans.length) {
+                        tabs = new TabStopSpan[spans.length];
+                    }
+                    for (int i = 0; i < spans.length; ++i) {
+                        if (spans[i] instanceof TabStopSpan) {
+                            tabs[tabLen++] = (TabStopSpan) spans[i];
+                        }
+                    }
+                }
+
+                if (tabs != null && tabLen < tabs.length){
+                    tabs[tabLen] = null;
+                }
+                mTabs = tabs;
+            }
+        }
+    }
+
+    /**
+     * 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, target, mLen, false, fmi);
+            }
+            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
+                return measureRun(0, 0, target, 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, PREP_NONE);
+            return w;
+        }
+
+        return handleRun(runIndex, start, limit, limit, runIsRtl, c, x, top,
+                y, bottom, null, needWidth, PREP_NEEDED);
+    }
+
+    /**
+     * 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, PREP_NEEDED);
+    }
+
+    /**
+     * Prepares a run for measurement or rendering.  This ensures that any
+     * required shaping of the text in the run has been performed so that
+     * measurements reflect the shaped text.
+     *
+     * @param runIndex the run index
+     * @param start the line-relative start of the run
+     * @param limit the line-relative limit of the run
+     * @param runIsRtl true if the run is right-to-left
+     */
+    private void prepRun(int runIndex, int start, int limit,
+            boolean runIsRtl) {
+        handleRun(runIndex, start, limit, limit, runIsRtl, null, 0, 0, 0,
+                0, null, false, PREP_ONLY);
+    }
+
+    /**
+     * 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.
+     *
+     * <p>The paint is required because the region around the cursor might not
+     * have been formatted yet, and the valid positions can depend on the glyphs
+     * used to render the text, which in turn depends on the paint.
+     *
+     * @param paint the base paint of the line
+     * @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.
+                  prepRun(runIndex, runStart, runLimit, runIsRtl);
+                  newCaret = getOffsetBeforeAfter(runIndex, 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) {
+                prepRun(otherRunIndex, otherRunStart, otherRunLimit,
+                        otherRunIsRtl);
+                newCaret = getOffsetBeforeAfter(otherRunIndex,
+                        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 = getOffsetBeforeAfter(-1, cursor, advance);
+              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 run.
+     *
+     * @param runIndex the run index
+     * @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 offset, boolean after) {
+        // XXX note currently there is no special handling of zero-width
+        // combining marks, since the only analysis involves mock shaping.
+
+        boolean offEnd = offset == (after ? mLen : 0);
+        if (runIndex >= 0 && !offEnd && mCharsValid) {
+            char[] chars = mChars;
+            if (after) {
+                int cp = Character.codePointAt(chars, offset, mLen);
+                if (cp >= 0x10000) {
+                    ++offset;
+                }
+                while (++offset < mLen && chars[offset] == '\ufeff'){}
+            } else {
+                while (--offset >= 0 && chars[offset] == '\ufeff'){}
+                int cp = Character.codePointBefore(chars, offset + 1);
+                if (cp >= 0x10000) {
+                    --offset;
+                }
+            }
+            return offset;
+        }
+
+        if (after) {
+            return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart;
+        }
+        return TextUtils.getOffsetBefore(mText, offset + mStart) - 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 limit the limit 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 limit,
+            boolean runIsRtl, Canvas c, float x, int top, int y, int bottom,
+            FontMetricsInt fmi, boolean needWidth) {
+
+        float ret = 0;
+
+        int runLen = limit - start;
+        if (needWidth || (c != null && (wp.bgColor != 0 || runIsRtl))) {
+            if (mCharsValid) {
+                ret = wp.measureText(mChars, start, runLen);
+            } else {
+                ret = wp.measureText(mText, mStart + start,
+                        mStart + start + runLen);
+            }
+        }
+
+        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, limit, 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
+     * @param prepFlags one of PREP_NONE, PREP_REQUIRED, or PREP_ONLY
+     * @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, int prepFlags) {
+
+        float ret = 0;
+
+        // Preparation replaces the first character of the series with the
+        // object-replacement character and the remainder with zero width
+        // non-break space aka BOM.  Cursor movement code skips over the BOMs
+        // so that the replacement character is the only character 'seen'.
+        if (prepFlags != PREP_NONE && limit > start &&
+                (runIndex > mPreppedIndex ||
+                        (runIndex == mPreppedIndex && start >= mPreppedLimit))) {
+            char[] chars = mChars;
+            chars[start] = '\ufffc';
+            for (int i = start + 1; i < limit; ++i) {
+                chars[i] = '\ufeff'; // used as ZWNBS, marks positions to skip
+            }
+            mPreppedIndex = runIndex;
+            mPreppedLimit = limit;
+        }
+
+        if (prepFlags != PREP_ONLY) {
+            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 p the base paint
+     * @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 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
+     * @param prepFlags one of PREP_NONE, PREP_REQUIRED, or PREP_ONLY
+     * @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 offset,
+            int limit, boolean runIsRtl, Canvas c, float x, int top, int y,
+            int bottom, FontMetricsInt fmi, boolean needWidth, int prepFlags) {
+
+        // 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, shape using the initial
+        // paint (the same typeface is used up to the next metric boundary),
+        // then within each metric run iterate through character style runs.
+        float ox = x;
+        for (int i = start, inext; i < offset; i = inext) {
+            TextPaint wp = mWorkPaint;
+            wp.set(mPaint);
+
+            int mnext;
+            if (mSpanned == null) {
+                inext = limit;
+                mnext = offset;
+            } else {
+                inext = mSpanned.nextSpanTransition(mStart + i, mStart + limit,
+                        MetricAffectingSpan.class) - mStart;
+
+                mnext = inext < offset ? inext : offset;
+                MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + i,
+                        mStart + mnext, 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.updateDrawState(wp); // XXX or measureState?
+                        }
+                    }
+
+                    if (replacement != null) {
+                        x += handleReplacement(replacement, wp, runIndex, i,
+                                mnext, runIsRtl, c, x, top, y, bottom, fmi,
+                                needWidth || mnext < offset, prepFlags);
+                        continue;
+                    }
+                }
+            }
+
+            if (prepFlags != PREP_NONE) {
+                handlePrep(wp, runIndex, i, inext, runIsRtl);
+            }
+
+            if (prepFlags != PREP_ONLY) {
+                if (mSpanned == null || c == null) {
+                    x += handleText(wp, i, mnext, runIsRtl, c, x, top,
+                            y, bottom, fmi, needWidth || mnext < offset);
+                } else {
+                    for (int j = i, jnext; j < mnext; j = jnext) {
+                        jnext = mSpanned.nextSpanTransition(mStart + j,
+                                mStart + mnext, 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, runIsRtl, c, x,
+                                top, y, bottom, fmi, needWidth || jnext < offset);
+                    }
+                }
+            }
+        }
+
+        return x - ox;
+    }
+
+    private static final int PREP_NONE = 0;
+    private static final int PREP_NEEDED = 1;
+    private static final int PREP_ONLY = 2;
+
+    /**
+     * Prepares text for measuring or rendering.
+     *
+     * @param paint the paint used to shape the text
+     * @param runIndex the run index
+     * @param start the start of the text to prepare
+     * @param limit the limit of the text to prepare
+     * @param runIsRtl true if the run is right-to-left
+     */
+    private void handlePrep(TextPaint paint, int runIndex, int start, int limit,
+            boolean runIsRtl) {
+
+        // The current implementation 'prepares' text by manipulating the
+        // character array.  In order to keep track of what ranges have
+        // already been prepared, it uses the runIndex and the limit of
+        // the prepared text within that run.  This index is required
+        // since operations that prepare the text always proceed in visual
+        // order and the limit itself does not let us know which runs have
+        // been processed and which have not.
+        //
+        // This bookkeeping is an attempt to let us process a line partially,
+        // for example, by only shaping up to the cursor position.  This may
+        // not make sense if we can reuse the line, say by caching repeated
+        // accesses to the same line for both measuring and drawing, since in
+        // those cases we'd always prepare the entire line.  At the
+        // opposite extreme, we might shape and then immediately discard only
+        // the run of text we're working with at the moment, instead of retaining
+        // the results of shaping (as the chars array is).  In this case as well
+        // we would not need to do the index/limit bookkeeping.
+        //
+        // Technically, the only reason for bookkeeping is so that we don't
+        // re-mirror already-mirrored glyphs, since the shaping and object
+        // replacement operations will not change already-processed text.
+
+        if (runIndex > mPreppedIndex ||
+                (runIndex == mPreppedIndex && start >= mPreppedLimit)) {
+            if (runIsRtl) {
+                int runLen = limit - start;
+                AndroidCharacter.mirror(mChars, start, runLen);
+                ArabicShaping.SHAPER.shape(mChars, start, runLen);
+
+                // Note: tweaked MockShaper to put '\ufeff' in place of
+                // alef when it forms lam-alef ligatures, so no extra
+                // processing is necessary here.
+            }
+            mPreppedIndex = runIndex;
+            mPreppedLimit = limit;
+        }
+    }
+
+    /**
+     * 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 run start
+     * @param limit the run limit
+     * @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 limit,
+            boolean runIsRtl, float x, int y) {
+
+        // Since currently skia only renders text left-to-right, we need to
+        // put the shaped characters into visual order before rendering.
+        // Since we might want to re-render the line again, we swap them
+        // back when we're done.  If we left them swapped, measurement
+        // would be broken since it expects the characters in logical order.
+        if (runIsRtl) {
+            swapRun(start, limit);
+        }
+        if (mCharsValid) {
+            c.drawText(mChars, start, limit - start, x, y, wp);
+        } else {
+            c.drawText(mText, mStart + start, mStart + limit, x, y, wp);
+        }
+        if (runIsRtl) {
+            swapRun(start, limit);
+        }
+    }
+
+    /**
+     * Reverses the order of characters in the chars array between start and
+     * limit, used by drawTextRun.
+     * @param start the start of the run to reverse
+     * @param limit the limit of the run to reverse
+     */
+    private void swapRun(int start, int limit) {
+        // First we swap all the characters one for one, then we
+        // do another pass looking for surrogate pairs and swapping them
+        // back into their logical order.
+        char[] chars = mChars;
+        for (int s = start, e = limit - 1; s < e; ++s, --e) {
+            char ch = chars[s]; chars[s] = chars[e]; chars[e] = ch;
+        }
+
+        for (int s = start, e = limit - 1; s < e; ++s) {
+            char c1 = chars[s];
+            if (c1 >= 0xdc00 && c1 < 0xe000) {
+                char c2 = chars[s+1];
+                if (c2 >= 0xd800 && c2 < 0xdc00) {
+                    chars[s++] = c2;
+                    chars[s] = c1;
+                }
+            }
+        }
+    }
+
+    /**
+     * 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) {
+        float nh = Float.MAX_VALUE;
+        boolean alltabs = false;
+
+        if (mHasTabs && mTabs != null) {
+            TabStopSpan[] tabs = mTabs;
+            for (int i = 0; i < tabs.length && tabs[i] != null; ++i) {
+                int where = tabs[i].getTabStop();
+                if (where < nh && where > h) {
+                    nh = where;
+                }
+            }
+            if (nh != Float.MAX_VALUE) {
+                return nh;
+            }
+        }
+
+        return ((int) ((h + TAB_INCREMENT) / TAB_INCREMENT)) * 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 9589bf3..2d6c7b6 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
@@ -1651,7 +1507,7 @@
 
         return mode;
     }
-    
+
     private static Object sLock = new Object();
     private static char[] sTemp = null;
 }
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index 2ee6e8a..5cbfd29 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -124,7 +124,7 @@
         + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
         + "|[1-9][0-9]|[0-9])))"
         + "(?:\\:\\d{1,5})?)" // plus option port number
-        + "(\\/(?:(?:[a-zA-Z0-9\\;\\/\\?\\:\\@\\&\\=\\#\\~"  // plus option query params
+        + "(\\/(?:(?:[" + GOOD_IRI_CHAR + "\\;\\/\\?\\:\\@\\&\\=\\#\\~"  // plus option query params
         + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?"
         + "(?:\\b|$)"); // and finally, a word boundary or end of
                         // input.  This is to stop foo.sure from
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index c22f991..fc61700 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -622,6 +622,7 @@
         mPackageName = null;
         mContentDescription = null;
         mBeforeText = null;
+        mParcelableData = null;
         mText.clear();
     }
 
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/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index b767f11..6958f5c 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -296,13 +296,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.
@@ -327,6 +327,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.
@@ -336,32 +338,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.
@@ -379,11 +402,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("-");
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 19abec1..c809b5a 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -300,6 +300,33 @@
         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);
+        }
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         // onDraw should only be called for password fields.  If WebTextView is
@@ -360,19 +387,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);
@@ -667,6 +683,7 @@
         } else {
             Selection.setSelection(text, selection, selection);
         }
+        if (mWebView != null) mWebView.incrementTextGeneration();
     }
 
     /**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6f4c6ff..76dde76 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -312,6 +312,8 @@
     // more key events.
     private int mTextGeneration;
 
+    /* package */ void incrementTextGeneration() { mTextGeneration++; }
+
     // Used by WebViewCore to create child views.
     /* package */ final ViewManager mViewManager;
 
@@ -3154,6 +3156,7 @@
         if (AUTO_REDRAW_HACK && mAutoRedraw) {
             invalidate();
         }
+        if (inEditingMode()) mWebTextView.onDrawSubstitute();
         mWebViewCore.signalRepaintDone();
     }
 
@@ -3978,6 +3981,14 @@
         super.onDetachedFromWindow();
     }
 
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        if (visibility != View.VISIBLE) {
+            dismissZoomControl();
+        }
+    }
+
     /**
      * @deprecated WebView no longer needs to implement
      * ViewGroup.OnHierarchyChangeListener.  This method does nothing now.
@@ -4683,16 +4694,13 @@
                         ted.mY = 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.sendMessageDelayed(mPrivateHandler
                                     .obtainMessage(PREVENT_DEFAULT_TIMEOUT,
@@ -4718,20 +4726,17 @@
                 // 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.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,
@@ -4762,9 +4767,11 @@
                         invalidate();
                         break;
                     }
+
                     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
@@ -4888,6 +4895,7 @@
                 break;
             }
             case MotionEvent.ACTION_UP: {
+                if (!isFocused()) requestFocus();
                 // pass the touch events from UI thread to WebCore thread
                 if (shouldForwardTouchEvent()) {
                     TouchEventData ted = new TouchEventData();
@@ -4896,10 +4904,6 @@
                     ted.mY = contentY;
                     ted.mMetaState = ev.getMetaState();
                     ted.mReprocess = mDeferTouchProcess;
-                    if (mDeferTouchProcess) {
-                        ted.mViewX = x;
-                        ted.mViewY = y;
-                    }
                     mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                 }
                 mLastTouchUpTime = eventTime;
@@ -4914,10 +4918,6 @@
                             ted.mY = 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();
@@ -4939,9 +4939,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();
@@ -6201,10 +6209,6 @@
                         // 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;
@@ -6385,14 +6389,8 @@
                     break;
                 case REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID:
                     displaySoftKeyboard(true);
-                    updateTextSelectionFromMessage(msg.arg1, msg.arg2,
-                            (WebViewCore.TextSelectionData) msg.obj);
-                    break;
+                    // fall through to UPDATE_TEXT_SELECTION_MSG_ID
                 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);
                     break;
@@ -6501,27 +6499,31 @@
                         TouchEventData ted = (TouchEventData) msg.obj;
                         switch (ted.mAction) {
                             case MotionEvent.ACTION_DOWN:
-                                mLastDeferTouchX = ted.mViewX;
-                                mLastDeferTouchY = ted.mViewY;
+                                mLastDeferTouchX = contentToViewX(ted.mX)
+                                        - mScrollX;
+                                mLastDeferTouchY = contentToViewY(ted.mY)
+                                        - mScrollY;
                                 mDeferTouchMode = TOUCH_INIT_MODE;
                                 break;
                             case MotionEvent.ACTION_MOVE: {
                                 // no snapping in defer process
+                                int x = contentToViewX(ted.mX) - mScrollX;
+                                int y = contentToViewY(ted.mY) - 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:
@@ -6535,8 +6537,8 @@
                                 break;
                             case WebViewCore.ACTION_DOUBLETAP:
                                 // doDoubleTap() needs mLastTouchX/Y as anchor
-                                mLastTouchX = ted.mViewX;
-                                mLastTouchY = ted.mViewY;
+                                mLastTouchX = contentToViewX(ted.mX) - mScrollX;
+                                mLastTouchY = contentToViewY(ted.mY) - mScrollY;
                                 doDoubleTap();
                                 mDeferTouchMode = TOUCH_DONE_MODE;
                                 break;
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 4118119..28b5ae6 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -708,8 +708,6 @@
         int mY;
         int mMetaState;
         boolean mReprocess;
-        float mViewX;
-        float mViewY;
     }
 
     static class GeolocationPermissionsData {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 48e7f79..0a39ab6 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -559,6 +559,16 @@
 
         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));
+                }
+            });
+        }
 
         a.recycle();
     }
diff --git a/core/java/android/widget/Adapters.java b/core/java/android/widget/Adapters.java
new file mode 100644
index 0000000..05e501a
--- /dev/null
+++ b/core/java/android/widget/Adapters.java
@@ -0,0 +1,1191 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.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 org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import static com.android.internal.R.*;
+
+/**
+ * <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" />
+ * <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" />
+ * <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" />
+ * <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 implicitely
+ * selected.</p>
+ * 
+ * <a name="xml-cursor-adapter-bind-tag" />
+ * <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.</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> chilren
+ * tags.</p>
+ *
+ * <a name="xml-cursor-adapter-bind-data-types" />
+ * <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>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
+ *  app widget</li>
+ * </ul>
+ * 
+ * <a name="xml-cursor-adapter-bind-transformation" />
+ * <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" />
+ * <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
+ *  transformationscannot 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 <" + name + " />");
+            }
+    
+            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_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, styleable.CursorAdapter);
+            
+            String uri = a.getString(styleable.CursorAdapter_uri);
+            String selection = a.getString(styleable.CursorAdapter_selection);
+            String sortOrder = a.getString(styleable.CursorAdapter_sortOrder);
+            int layout = a.getResourceId(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, styleable.CursorAdapter_SelectItem);
+    
+            String fromName = a.getString(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, styleable.CursorAdapter_BindItem);
+    
+            String fromName = a.getString(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(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(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_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, styleable.CursorAdapter_MapItem);
+
+            String from = a.getString(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(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(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, styleable.CursorAdapter_TransformItem);
+            
+            String className = a.getString(styleable.CursorAdapter_TransformItem_withClass);
+            if (className == null) {
+                String expression = a.getString(
+                        styleable.CursorAdapter_TransformItem_withExpression);
+                transformation = createExpressionTransformation(expression);
+            } else if (!mContext.isRestricted()) {
+                try {
+                    final Class<?> klass = Class.forName(className, true, mContext.getClassLoader());
+                    if (CursorTransformation.class.isAssignableFrom(klass)) {
+                        final Constructor<?> c = klass.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 final Context mContext;
+        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) {
+            ((TextView) view).setText(mTransformation.transform(cursor, columnIndex));
+            return true;
+        }
+    }
+
+    /**
+     * 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) {
+            final byte[] data = cursor.getBlob(columnIndex);
+            ((ImageView) view).setImageBitmap(BitmapFactory.decodeByteArray(data, 0, data.length));
+            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) {
+            ((ImageView) view).setImageURI(Uri.parse(
+                    mTransformation.transform(cursor, columnIndex)));
+            return true;
+        }
+    }
+
+    /**
+     * 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) {
+            final int resource = mTransformation.transformToResource(cursor, columnIndex);
+            if (resource == 0) return false;
+
+            ((ImageView) view).setImageResource(resource);
+            return true;
+        }
+    }
+}
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index e15a520..8611901 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -913,10 +913,10 @@
 
             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());
+                final int position = completion.getPosition();
+                mItemClickListener.onItemClick(list,
+                        list.getChildAt(position - list.getFirstVisiblePosition()),
+                        position, completion.getId());
             }
         }
     }
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/Gallery.java b/core/java/android/widget/Gallery.java
index f34823c..6775a94 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -1207,7 +1207,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);
@@ -1263,6 +1263,7 @@
          */
         if (gainFocus && mSelectedChild != null) {
             mSelectedChild.requestFocus(direction);
+            mSelectedChild.setSelected(true);
         }
 
     }
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index 07c3e4b..50fbb6b 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -48,6 +48,7 @@
     private QueryHandler mQueryHandler;
     private Drawable mBadgeBackground;
     private Drawable mNoBadgeBackground;
+    private Drawable mDefaultAvatar;
 
     protected String[] mExcludeMimes = null;
 
@@ -117,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
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/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/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/widget/ContactHeaderWidget.java b/core/java/com/android/internal/widget/ContactHeaderWidget.java
index f421466..a514089 100644
--- a/core/java/com/android/internal/widget/ContactHeaderWidget.java
+++ b/core/java/com/android/internal/widget/ContactHeaderWidget.java
@@ -55,7 +55,7 @@
 /**
  * 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.
+ * {@link #bindFromContactLookupUri(Uri)} to populate asynchronously.
  * <p>
  * The parent must request the {@link Manifest.permission#READ_CONTACTS}
  * permission to access contact data.
@@ -257,7 +257,7 @@
                         if (photoBitmap == null) {
                             photoBitmap = loadPlaceholderPhoto(null);
                         }
-                        mPhotoView.setImageBitmap(photoBitmap);
+                        setPhoto(photoBitmap);
                         if (cookie != null && cookie instanceof Uri) {
                             mPhotoView.assignContactUri((Uri) cookie);
                         }
@@ -267,21 +267,13 @@
                     case TOKEN_CONTACT_INFO: {
                         if (cursor != null && cursor.moveToFirst()) {
                             bindContactInfo(cursor);
-                            Uri lookupUri = Contacts.getLookupUri(cursor.getLong(ContactQuery._ID),
+                            final 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 */);
-                            }
+                            setPhotoId(photoId, lookupUri);
                         } else {
                             // shouldn't really happen
                             setDisplayName(null, null);
@@ -361,18 +353,40 @@
     }
 
     /**
-     * Manually set the contact uri
+     * Manually set the presence. If presence is null, it is hidden.
+     * This doesn't change the underlying {@link Contacts} value, only the UI state.
+     * @hide
+     */
+    public void setPresence(Integer presence) {
+        if (presence == null) {
+            showPresence(false);
+        } else {
+            showPresence(true);
+            setPresence(presence.intValue());
+        }
+    }
+
+    /**
+     * Turn on/off showing the presence.
+     * @hide this is here for consistency with setStared/showStar and should be public
+     */
+    public void showPresence(boolean showPresence) {
+        mPresenceView.setVisibility(showPresence ? View.VISIBLE : View.GONE);
+    }
+
+    /**
+     * Manually set the contact uri without loading any data
      */
     public void setContactUri(Uri uri) {
         setContactUri(uri, true);
     }
 
     /**
-     * Manually set the contact uri
+     * Manually set the contact uri without loading any data
      */
-    public void setContactUri(Uri uri, boolean sendToFastrack) {
+    public void setContactUri(Uri uri, boolean sendToQuickContact) {
         mContactUri = uri;
-        if (sendToFastrack) {
+        if (sendToQuickContact) {
             mPhotoView.assignContactUri(uri);
         }
     }
@@ -386,6 +400,22 @@
     }
 
     /**
+     * Manually set the photo given its id. If the id is 0, a placeholder picture will
+     * be loaded. For any other Id, an async query is started
+     * @hide
+     */
+    public void setPhotoId(final long photoId, final Uri lookupUri) {
+        if (photoId == 0) {
+            setPhoto(loadPlaceholderPhoto(null));
+            mPhotoView.assignContactUri(lookupUri);
+            invalidate();
+        } else {
+            startPhotoQuery(photoId, lookupUri,
+                    false /* don't reset query handler */);
+        }
+    }
+
+    /**
      * 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.
      */
@@ -400,7 +430,8 @@
     }
 
     /**
-     * Manually set the social snippet text to display in the header.
+     * Manually set the social snippet text to display in the header. This doesn't change the
+     * underlying {@link Contacts}, only the UI state.
      */
     public void setSocialSnippet(CharSequence snippet) {
         if (snippet == null) {
@@ -413,6 +444,20 @@
     }
 
     /**
+     * Manually set the status attribution text to display in the header.
+     * This doesn't change the underlying {@link Contacts}, only the UI state.
+     * @hide
+     */
+    public void setStatusAttribution(CharSequence attribution) {
+        if (attribution != null) {
+            mStatusAttributionView.setText(attribution);
+            mStatusAttributionView.setVisibility(View.VISIBLE);
+        } else {
+            mStatusAttributionView.setVisibility(View.GONE);
+        }
+    }
+
+    /**
      * 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.
@@ -423,6 +468,88 @@
     }
 
     /**
+     * Manually set all the status values to display in the header.
+     * This doesn't change the underlying {@link Contacts}, only the UI state.
+     * @hide
+     * @param status             The status of the contact. If this is either null or empty,
+     *                           the status is cleared and the other parameters are ignored.
+     * @param statusTimestamp    The timestamp (retrieved via a call to
+     *                           {@link System#currentTimeMillis()}) of the last status update.
+     *                           This value can be null if it is not known.
+     * @param statusLabel        The id of a resource string that specifies the current
+     *                           status. This value can be null if no Label should be used.
+     * @param statusResPackage   The name of the resource package containing the resource string
+     *                           referenced in the parameter statusLabel.
+     */
+    public void setStatus(final String status, final Long statusTimestamp,
+            final Integer statusLabel, final String statusResPackage) {
+        if (TextUtils.isEmpty(status)) {
+            setSocialSnippet(null);
+            return;
+        }
+
+        setSocialSnippet(status);
+
+        final CharSequence timestampDisplayValue;
+
+        if (statusTimestamp != null) {
+            // Set the date/time field by mixing relative and absolute
+            // times.
+            int flags = DateUtils.FORMAT_ABBREV_RELATIVE;
+
+            timestampDisplayValue = DateUtils.getRelativeTimeSpanString(
+                    statusTimestamp.longValue(), System.currentTimeMillis(),
+                    DateUtils.MINUTE_IN_MILLIS, flags);
+        } else {
+            timestampDisplayValue = null;
+        }
+
+
+        String labelDisplayValue = null;
+
+        if (statusLabel != null) {
+            Resources resources;
+            if (TextUtils.isEmpty(statusResPackage)) {
+                resources = getResources();
+            } else {
+                PackageManager pm = getContext().getPackageManager();
+                try {
+                    resources = pm.getResourcesForApplication(statusResPackage);
+                } catch (NameNotFoundException e) {
+                    Log.w(TAG, "Contact status update resource package not found: "
+                            + statusResPackage);
+                    resources = null;
+                }
+            }
+
+            if (resources != null) {
+                try {
+                    labelDisplayValue = resources.getString(statusLabel.intValue());
+                } catch (NotFoundException e) {
+                    Log.w(TAG, "Contact status update resource not found: " + statusResPackage + "@"
+                            + statusLabel.intValue());
+                }
+            }
+        }
+
+        final CharSequence attribution;
+        if (timestampDisplayValue != null && labelDisplayValue != null) {
+            attribution = getContext().getString(
+                    R.string.contact_status_update_attribution_with_date,
+                    timestampDisplayValue, labelDisplayValue);
+        } else if (timestampDisplayValue == null && labelDisplayValue != null) {
+            attribution = getContext().getString(
+                    R.string.contact_status_update_attribution,
+                    labelDisplayValue);
+        } else if (timestampDisplayValue != null) {
+            attribution = timestampDisplayValue;
+        } else {
+            attribution = null;
+        }
+        setStatusAttribution(attribution);
+    }
+
+    /**
      * Convenience method for binding all available data from an existing
      * contact.
      *
@@ -543,89 +670,28 @@
         this.setDisplayName(displayName, phoneticName);
 
         final boolean starred = c.getInt(ContactQuery.STARRED) != 0;
-        mStarredView.setChecked(starred);
+        setStared(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);
+            setPresence(presence);
+            showPresence(true);
         } else {
-            mPresenceView.setVisibility(View.GONE);
+            showPresence(false);
         }
 
         //Set the status update
-        String status = c.getString(ContactQuery.CONTACT_STATUS);
-        if (!TextUtils.isEmpty(status)) {
-            mStatusView.setText(status);
-            mStatusView.setVisibility(View.VISIBLE);
+        final String status = c.getString(ContactQuery.CONTACT_STATUS);
+        final Long statusTimestamp = c.isNull(ContactQuery.CONTACT_STATUS_TIMESTAMP)
+                ? null
+                : c.getLong(ContactQuery.CONTACT_STATUS_TIMESTAMP);
+        final Integer statusLabel = c.isNull(ContactQuery.CONTACT_STATUS_LABEL)
+                ? null
+                : c.getInt(ContactQuery.CONTACT_STATUS_LABEL);
+        final String statusResPackage = c.getString(ContactQuery.CONTACT_STATUS_RES_PACKAGE);
 
-            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);
-        }
+        setStatus(status, statusTimestamp, statusLabel, statusResPackage);
     }
 
     public void onClick(View view) {
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/res/assets/images/combobox-disabled.png b/core/res/assets/images/combobox-disabled.png
deleted file mode 100644
index fe220e4..0000000
--- a/core/res/assets/images/combobox-disabled.png
+++ /dev/null
Binary files differ
diff --git a/core/res/assets/images/combobox-noHighlight.png b/core/res/assets/images/combobox-noHighlight.png
deleted file mode 100644
index abcdf72..0000000
--- a/core/res/assets/images/combobox-noHighlight.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/combobox_disabled.png b/core/res/res/drawable-hdpi/combobox_disabled.png
new file mode 100644
index 0000000..50eb45e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/combobox_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/combobox_nohighlight.png b/core/res/res/drawable-hdpi/combobox_nohighlight.png
new file mode 100644
index 0000000..9d60301
--- /dev/null
+++ b/core/res/res/drawable-hdpi/combobox_nohighlight.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/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-mdpi/combobox_disabled.png b/core/res/res/drawable-mdpi/combobox_disabled.png
new file mode 100644
index 0000000..c32db7e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/combobox_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/combobox_nohighlight.png b/core/res/res/drawable-mdpi/combobox_nohighlight.png
new file mode 100644
index 0000000..1963316
--- /dev/null
+++ b/core/res/res/drawable-mdpi/combobox_nohighlight.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/ic_btn_back.png b/core/res/res/drawable/ic_btn_back.png
new file mode 100644
index 0000000..c9bff4c
--- /dev/null
+++ b/core/res/res/drawable/ic_btn_back.png
Binary files differ
diff --git a/core/res/res/drawable/ic_btn_next.png b/core/res/res/drawable/ic_btn_next.png
new file mode 100755
index 0000000..c6cf436
--- /dev/null
+++ b/core/res/res/drawable/ic_btn_next.png
Binary files differ
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/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/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index 8f86981..844d338 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -4,22 +4,58 @@
 **
 ** 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">
+
+    <ListView android:id="@android:id/list"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:layout_weight="1"
+        android:drawSelectorOnTop="false"
+        android:scrollbarAlwaysDrawVerticalTrack="true"
     />
+
+    <RelativeLayout android:id="@+id/button_bar"
+        android:layout_height="wrap_content"
+        android:layout_width="fill_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"
+        />
+
+        <Button android:id="@+id/next_button"
+            android:layout_width="150dip"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:layout_alignParentRight="true"
+            android:drawableRight="@drawable/ic_btn_next"
+            android:drawablePadding="3dip"
+            android:text="@string/next_button_label"
+        />
+    </RelativeLayout>
+</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a3ccaf7..67a88ae 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1518,6 +1518,8 @@
              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" />
     </declare-styleable>
     <declare-styleable name="AbsSpinner">
         <!-- Reference to an array resource that will populate the Spinner.  For static content,
@@ -2504,6 +2506,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" />
@@ -3337,6 +3343,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. -->
@@ -3644,5 +3660,68 @@
     <declare-styleable name="RecognitionService">
         <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>
 
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 26d11cd..03152b1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1238,7 +1238,7 @@
   <public type="id" name="custom" id="0x0102002b" />
     
   <public type="anim" name="cycle_interpolator" id="0x010a000c" />
-
+    
 <!-- ===============================================================
      Resources introduced in kraken.
      =============================================================== -->
@@ -1257,4 +1257,21 @@
   <public-padding type="color" name="kraken_resource_pad" end="0x01060020" />
   <public-padding type="array" name="kraken_resource_pad" end="0x01070010" />
 
+<!-- ===============================================================
+     Resources proposed for Gingerbread.
+     =============================================================== -->
+  <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" />
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a76c70c..f37a927 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2260,6 +2260,10 @@
     <string name="tethered_notification_title">Tethering 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>
+
     <!-- 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>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index b5fff96..f9b0667 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -552,6 +552,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">
diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
index 656029d..34434d2 100644
--- a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
@@ -33,6 +33,7 @@
 import junit.framework.Assert;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Locale;
 
@@ -1023,6 +1024,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 +1137,44 @@
                 }
             }
         }
-    }    
+    }
+
+    @SmallTest
+    public void testLruCachingOfSqliteCompiledSqlObjs() {
+        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);
+
+        // 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<SQLiteStatement> stmtObjs = new ArrayList<SQLiteStatement>();
+        for (int i = 0; i < N+1; i++) {
+            SQLiteStatement c = mDatabase.compileStatement("select * from test where i = " + i);
+            c.close();
+            stmtObjs.add(i, c);
+        }
+
+        assertEquals(0, stmtObjs.get(0).getUniqueId());
+        for (int i = 1; i < N+1; i++) {
+            assertTrue(stmtObjs.get(i).getUniqueId() > 0);
+        }
+    }
+
+    @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"));
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java
index 004a197..0820a68 100644
--- a/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java
+++ b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java
@@ -17,7 +17,9 @@
 package android.pim.vcard;
 
 import android.content.ContentValues;
-import android.pim.vcard.VCardConfig;
+import android.pim.vcard.test_utils.ContactEntry;
+import android.pim.vcard.test_utils.PropertyNodesVerifierElem;
+import android.pim.vcard.test_utils.PropertyNodesVerifierElem.TypeSet;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Event;
 import android.provider.ContactsContract.CommonDataKinds.Im;
@@ -31,8 +33,6 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
 
-import android.pim.vcard.PropertyNodesVerifierElem.TypeSet;
-
 import java.util.Arrays;
 
 /**
@@ -275,6 +275,16 @@
         testPhoneBasicCommon(V30);
     }
 
+    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.
      */
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
index 21f2254..ea2ac6a 100644
--- a/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
+++ b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
@@ -16,7 +16,9 @@
 package android.pim.vcard;
 
 import android.content.ContentValues;
-import android.pim.vcard.VCardConfig;
+import android.pim.vcard.test_utils.ContentValuesVerifier;
+import android.pim.vcard.test_utils.ContentValuesVerifierElem;
+import android.pim.vcard.test_utils.PropertyNodesVerifierElem.TypeSet;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Event;
@@ -29,7 +31,6 @@
 import android.provider.ContactsContract.CommonDataKinds.Website;
 
 import com.android.frameworks.coretests.R;
-import android.pim.vcard.PropertyNodesVerifierElem.TypeSet;
 
 import java.util.Arrays;
 
@@ -410,7 +411,7 @@
     }
 
     public void testV21SimpleCase1_Type_Generic() {
-        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8, R.raw.v21_simple_1);
+        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC, R.raw.v21_simple_1);
         mVerifier.addContentValuesVerifierElem()
                 .addExpected(StructuredName.CONTENT_ITEM_TYPE)
                         .put(StructuredName.FAMILY_NAME, "Ando")
@@ -419,7 +420,7 @@
     }
 
     public void testV21SimpleCase1_Type_Japanese() {
-        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, R.raw.v21_simple_1);
+        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_simple_1);
         mVerifier.addContentValuesVerifierElem()
                 .addExpected(StructuredName.CONTENT_ITEM_TYPE)
                         .put(StructuredName.FAMILY_NAME, "Ando")
@@ -431,7 +432,7 @@
     }
 
     public void testV21SimpleCase2() {
-        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, R.raw.v21_simple_2);
+        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");
@@ -717,8 +718,7 @@
         // Though Japanese careers append ";;;;" at the end of the value of "SOUND",
         // vCard 2.1/3.0 specification does not allow multiple values.
         // Do not need to handle it as multiple values.
-        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
-                R.raw.v21_japanese_1);
+        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_japanese_1);
         mVerifier.addPropertyNodesVerifierElem()
                 .addExpectedNodeWithOrder("VERSION", "2.1", null, null, null, null, null)
                 .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9;;;;",
@@ -752,35 +752,34 @@
 
     /**
      * Verifies vCard with Japanese can be parsed correctly with
-     * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_GENERIC_UTF8}.
+     * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_GENERIC}.
      */
     public void testV21Japanese1_Type_Generic_Utf8() {
         testV21Japanese1Common(
-                R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8, false);
+                R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_GENERIC, false);
     }
 
     /**
      * Verifies vCard with Japanese can be parsed correctly with
-     * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE_SJIS}.
+     * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE}.
      */
     public void testV21Japanese1_Type_Japanese_Sjis() {
         testV21Japanese1Common(
-                R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, true);
+                R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE, true);
     }
 
     /**
      * Verifies vCard with Japanese can be parsed correctly with
-     * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE_UTF8}.
+     * {@link android.pim.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_UTF8, true);
+                R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE, true);
     }
 
     public void testV21Japanese2_Parsing() {
-        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
-                R.raw.v21_japanese_2);
+        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_japanese_2);
         mVerifier.addPropertyNodesVerifierElem()
                 .addExpectedNodeWithOrder("VERSION", "2.1")
                 .addExpectedNodeWithOrder("N", "\u5B89\u85E4;\u30ED\u30A4\u30C9\u0031;;;",
@@ -838,8 +837,7 @@
     }
 
     public void testV21MultipleEntryCase_Parse() {
-        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
-                R.raw.v21_multiple_entry);
+        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_multiple_entry);
         mVerifier.addPropertyNodesVerifierElem()
                 .addExpectedNodeWithOrder("VERSION", "2.1")
                 .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033;;;;",
@@ -882,8 +880,7 @@
     }
 
     public void testV21MultipleEntryCase() {
-        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
-                R.raw.v21_multiple_entry);
+        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")
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java b/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java
index 5b60342..17ee322 100644
--- a/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java
+++ b/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java
@@ -17,15 +17,16 @@
 package android.pim.vcard;
 
 import android.content.ContentValues;
-import android.pim.vcard.VCardConfig;
+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.provider.ContactsContract.CommonDataKinds.Nickname;
 import android.provider.ContactsContract.CommonDataKinds.Note;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 
-import android.pim.vcard.PropertyNodesVerifierElem.TypeSet;
-
 import java.util.Arrays;
 
 public class VCardJapanizationTests extends VCardTestsBase {
@@ -50,15 +51,15 @@
     }
 
     public void testNameUtf8V21() {
-        testNameUtf8Common(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
+        testNameUtf8Common(VCardConfig.VCARD_TYPE_V21_JAPANESE);
     }
 
     public void testNameUtf8V30() {
-        testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
+        testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE);
     }
 
     public void testNameShiftJis() {
-        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_JAPANESE_SJIS);
+        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")
@@ -80,7 +81,7 @@
      * DoCoMo phones require all name elements should be in "family name" field.
      */
     public void testNameDoCoMo() {
-        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+        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")
@@ -105,8 +106,8 @@
                 .addExpectedNode("X-DCM-HMN-MODE", "");
     }
 
-    private void testPhoneticNameCommon(int vcardType) {
-        mVerifier.initForExportTest(vcardType);
+    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")
@@ -114,7 +115,7 @@
                 .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046");
 
         final ContentValues contentValues =
-            (VCardConfig.usesShiftJis(vcardType) ?
+            ("SHIFT_JIS".equalsIgnoreCase(charset) ?
                     (VCardConfig.isV30(vcardType) ? mContentValuesForSJis :
                             mContentValuesForQPAndSJis) :
                     (VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8));
@@ -142,23 +143,23 @@
     }
 
     public void testPhoneticNameForJapaneseV21Utf8() {
-        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
+        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, null);
     }
 
     public void testPhoneticNameForJapaneseV21Sjis() {
-        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
+        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, "Shift_JIS");
     }
 
     public void testPhoneticNameForJapaneseV30Utf8() {
-        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
+        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, null);
     }
 
     public void testPhoneticNameForJapaneseV30SJis() {
-        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_SJIS);
+        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS");
     }
 
     public void testPhoneticNameForMobileV21_1() {
-        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE);
+        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")
@@ -182,7 +183,7 @@
     }
 
     public void testPhoneticNameForMobileV21_2() {
-        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE);
+        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")
@@ -198,8 +199,8 @@
                 .put(StructuredName.DISPLAY_NAME, "\uFF94\uFF8F\uFF80\uFF9E \uFF80\uFF9B\uFF73");
     }
 
-    private void testPostalAddressWithJapaneseCommon(int vcardType) {
-        mVerifier.initForExportTest(vcardType);
+    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")
@@ -214,7 +215,7 @@
                 .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
                 .put(StructuredPostal.LABEL, "\u304A\u3082\u3061\u304B\u3048\u308A");
 
-        ContentValues contentValues = (VCardConfig.usesShiftJis(vcardType) ?
+        ContentValues contentValues = ("UTF-8".equalsIgnoreCase(charset) ?
                 (VCardConfig.isV30(vcardType) ? mContentValuesForSJis :
                     mContentValuesForQPAndSJis) :
                 (VCardConfig.isV30(vcardType) ? mContentValuesForUtf8 :
@@ -240,7 +241,7 @@
                 .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME);
     }
     public void testPostalAddresswithJapaneseV21() {
-        testPostalAddressWithJapaneseCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
+        testPostalAddressWithJapaneseCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, "Shift_JIS");
     }
 
     /**
@@ -248,7 +249,7 @@
      * Prefered type must (should?) be: HOME > WORK > OTHER > CUSTOM
      */
     public void testPostalAdrressForDoCoMo_1() {
-        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
         ContactEntry entry = mVerifier.addInputEntry();
         entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
                 .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
@@ -276,7 +277,7 @@
     }
 
     public void testPostalAdrressForDoCoMo_2() {
-        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
         ContactEntry entry = mVerifier.addInputEntry();
         entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
                 .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
@@ -301,7 +302,7 @@
     }
 
     public void testPostalAdrressForDoCoMo_3() {
-        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
         ContactEntry entry = mVerifier.addInputEntry();
         entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
                 .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
@@ -329,7 +330,7 @@
      * Verifies the vCard exporter tolerates null TYPE.
      */
     public void testPostalAdrressForDoCoMo_4() {
-        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
         ContactEntry entry = mVerifier.addInputEntry();
         entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
                 .put(StructuredPostal.POBOX, "1");
@@ -371,15 +372,15 @@
     }
 
     public void testJapanesePhoneNumberV21_1() {
-        testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
+        testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE);
     }
 
     public void testJapanesePhoneNumberV30() {
-        testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
+        testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE);
     }
 
     public void testJapanesePhoneNumberDoCoMo() {
-        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
         ContactEntry entry = mVerifier.addInputEntry();
         entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
                 .put(Phone.NUMBER, "0312341234")
@@ -399,7 +400,7 @@
     }
 
     public void testNoteDoCoMo() {
-        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
         ContactEntry entry = mVerifier.addInputEntry();
         entry.addContentValues(Note.CONTENT_ITEM_TYPE)
                 .put(Note.NOTE, "note1");
@@ -421,7 +422,7 @@
     }
 
     public void testAndroidCustomV21() {
-        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+        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()
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java b/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java
index 0857e0c..383a9af 100644
--- a/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java
+++ b/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java
@@ -16,103 +16,17 @@
 
 package android.pim.vcard;
 
-import android.content.ContentProvider;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
 import android.content.ContentValues;
-import android.content.EntityIterator;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.database.CursorWindow;
-import android.database.IBulkCursor;
-import android.database.IContentObserver;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.pim.vcard.VCardConfig;
+import android.pim.vcard.test_utils.VCardVerifier;
 import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-/**
- * Almost a dead copy of android.test.mock.MockContentProvider, but different in that this
- * class extends ContentProvider, not implementing IContentProvider,
- * so that MockContentResolver is able to accept this class :(
- */
-class MockContentProvider extends ContentProvider {
-    @Override
-    public boolean onCreate() {
-        return true;
-    }
-
-    @Override
-    public int bulkInsert(Uri url, ContentValues[] initialValues) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("unused")
-    public IBulkCursor bulkQuery(Uri url, String[] projection, String selection,
-            String[] selectionArgs, String sortOrder, IContentObserver observer,
-            CursorWindow window) throws RemoteException {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    @SuppressWarnings("unused")
-    public int delete(Uri url, String selection, String[] selectionArgs) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public String getType(Uri url) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public Uri insert(Uri url, ContentValues initialValues) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public ParcelFileDescriptor openFile(Uri url, String mode) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public AssetFileDescriptor openAssetFile(Uri uri, String mode) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @Override
-    public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    public IBinder asBinder() {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-}
 
 /**
  * BaseClass for vCard unit tests with utility classes.
  * Please do not add each unit test here.
  */
 /* package */ class VCardTestsBase extends AndroidTestCase {
-    public static final int V21 = VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8;
-    public static final int V30 = VCardConfig.VCARD_TYPE_V30_GENERIC_UTF8;
+    public static final int V21 = VCardConfig.VCARD_TYPE_V21_GENERIC;
+    public static final int V30 = VCardConfig.VCARD_TYPE_V30_GENERIC;
 
     // Do not modify these during tests.
     protected final ContentValues mContentValuesForQP;
@@ -128,6 +42,7 @@
 
     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();
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
new file mode 100644
index 0000000..843750e
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ContactEntry.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 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/ContentValuesBuilder.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesBuilder.java
similarity index 96%
rename from core/tests/coretests/src/android/pim/vcard/ContentValuesBuilder.java
rename to core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesBuilder.java
index b3c0773..5c24186 100644
--- a/core/tests/coretests/src/android/pim/vcard/ContentValuesBuilder.java
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesBuilder.java
@@ -13,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package android.pim.vcard;
+package android.pim.vcard.test_utils;
 
 import android.content.ContentValues;
 
@@ -22,7 +21,7 @@
  * ContentValues-like class which enables users to chain put() methods and restricts
  * the other methods.
  */
-/* package */ class ContentValuesBuilder {
+public class ContentValuesBuilder {
     private final ContentValues mContentValues;
 
     public ContentValuesBuilder(final ContentValues contentValues) {
diff --git a/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifier.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifier.java
similarity index 92%
rename from core/tests/coretests/src/android/pim/vcard/ContentValuesVerifier.java
rename to core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifier.java
index b9e9875..f2516ed 100644
--- a/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifier.java
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifier.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.pim.vcard;
+package android.pim.vcard.test_utils;
 
 import android.pim.vcard.VCardConfig;
 import android.pim.vcard.VCardEntry;
@@ -30,7 +30,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-/* package */ class ContentValuesVerifier implements VCardEntryHandler {
+public class ContentValuesVerifier implements VCardEntryHandler {
     private AndroidTestCase mTestCase;
     private List<ContentValuesVerifierElem> mContentValuesVerifierElemList =
         new ArrayList<ContentValuesVerifierElem>();
@@ -56,7 +56,7 @@
     public void verify(InputStream is, int vCardType) throws IOException, VCardException {
         final VCardParser vCardParser;
         if (VCardConfig.isV30(vCardType)) {
-            vCardParser = new VCardParser_V30(true);  // use StrictParsing
+            vCardParser = new VCardParser_V30();
         } else {
             vCardParser = new VCardParser_V21();
         }
@@ -66,7 +66,7 @@
     public void verify(InputStream is, int vCardType, final VCardParser vCardParser)
             throws IOException, VCardException {
         VCardEntryConstructor builder =
-            new VCardEntryConstructor(null, null, false, vCardType, null);
+            new VCardEntryConstructor(vCardType, null, null, false);
         builder.addEntryHandler(this);
         try {
             vCardParser.parse(is, builder);
diff --git a/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifierElem.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifierElem.java
similarity index 92%
rename from core/tests/coretests/src/android/pim/vcard/ContentValuesVerifierElem.java
rename to core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifierElem.java
index 2edbb36..66d69b3 100644
--- a/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifierElem.java
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifierElem.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.pim.vcard;
+package android.pim.vcard.test_utils;
 
 import android.content.ContentValues;
 import android.pim.vcard.VCardConfig;
@@ -31,7 +31,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 
-/* package */ class ContentValuesVerifierElem {
+public class ContentValuesVerifierElem {
     private final AndroidTestCase mTestCase;
     private final ImportTestResolver mResolver;
     private final VCardEntryHandler mHandler;
@@ -57,12 +57,12 @@
     public void verify(InputStream is, int vCardType) throws IOException, VCardException {
         final VCardParser vCardParser;
         if (VCardConfig.isV30(vCardType)) {
-            vCardParser = new VCardParser_V30(true);  // use StrictParsing
+            vCardParser = new VCardParser_V30();
         } else {
             vCardParser = new VCardParser_V21();
         }
         VCardEntryConstructor builder =
-                new VCardEntryConstructor(null, null, false, vCardType, null);
+                new VCardEntryConstructor(vCardType, null, null, false);
         builder.addEntryHandler(mHandler);
         try {
             vCardParser.parse(is, builder);
diff --git a/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ExportTestProvider.java
similarity index 63%
rename from core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java
rename to core/tests/coretests/src/android/pim/vcard/test_utils/ExportTestProvider.java
index 5968e83..0fbd9bb 100644
--- a/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ExportTestProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 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. You may obtain a copy of
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package android.pim.vcard;
+package android.pim.vcard.test_utils;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -21,10 +21,11 @@
 import android.content.EntityIterator;
 import android.database.Cursor;
 import android.net.Uri;
+import android.pim.vcard.VCardComposer;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
-import android.test.mock.MockContentResolver;
+import android.test.mock.MockContentProvider;
 import android.test.mock.MockCursor;
 
 import junit.framework.TestCase;
@@ -33,76 +34,44 @@
 import java.util.Iterator;
 import java.util.List;
 
-/* package */ public class ExportTestResolver extends MockContentResolver {
-    ExportTestProvider mProvider;
-    public ExportTestResolver(TestCase testCase) {
-        mProvider = new ExportTestProvider(testCase);
-        addProvider(VCardComposer.VCARD_TEST_AUTHORITY, mProvider);
-        addProvider(RawContacts.CONTENT_URI.getAuthority(), mProvider);
-    }
-
-    public ContactEntry addInputContactEntry() {
-        return mProvider.buildInputEntry();
-    }
-}
-
-/* package */ class MockEntityIterator implements EntityIterator {
-    List<Entity> mEntityList;
-    Iterator<Entity> mIterator;
-
-    public MockEntityIterator(List<ContentValues> contentValuesList) {
-        mEntityList = new ArrayList<Entity>();
-        Entity entity = new Entity(new ContentValues());
-        for (ContentValues contentValues : contentValuesList) {
-                entity.addSubValue(Data.CONTENT_URI, contentValues);
-        }
-        mEntityList.add(entity);
-        mIterator = mEntityList.iterator();
-    }
-
-    public boolean hasNext() {
-        return mIterator.hasNext();
-    }
-
-    public Entity next() {
-        return mIterator.next();
-    }
-
-    public void remove() {
-        throw new UnsupportedOperationException("remove not supported");
-    }
-
-    public void reset() {
-        mIterator = mEntityList.iterator();
-    }
-
-    public void close() {
-    }
-}
-
-/**
- * Represents one contact, which should contain multiple ContentValues like
- * StructuredName, Email, etc.
- */
-/* package */ class ContactEntry {
-    private final List<ContentValues> mContentValuesList = new ArrayList<ContentValues>();
-
-    public ContentValuesBuilder addContentValues(String mimeType) {
-        ContentValues contentValues = new ContentValues();
-        contentValues.put(Data.MIMETYPE, mimeType);
-        mContentValuesList.add(contentValues);
-        return new ContentValuesBuilder(contentValues);
-    }
-
-    public List<ContentValues> getList() {
-        return mContentValuesList;
-    }
-}
-
 /* package */ class ExportTestProvider extends MockContentProvider {
     final private TestCase mTestCase;
     final private ArrayList<ContactEntry> mContactEntryList = new ArrayList<ContactEntry>();
 
+    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(TestCase testCase) {
         mTestCase = testCase;
     }
@@ -121,16 +90,6 @@
      * We still keep using this method since we don't have a propeer way to know
      * which value in the ContentValue corresponds to the entry in Contacts database.
      * </p>
-     * <p>
-     * Detail:
-     * There's an easy way to know which index "family name" corresponds to, via
-     * {@link android.provider.ContactsContract}.
-     * FAMILY_NAME equals DATA3, so the corresponding index
-     * for "family name" should be 2 (note that index is 0-origin).
-     * However, we cannot know what the index 2 corresponds to; it may be "family name",
-     * "label" for now, but may be the other some column in the future. We don't have
-     * convenient way to know the original data structure.
-     * </p>
      */
     public EntityIterator queryEntities(Uri uri,
             String selection, String[] selectionArgs, String sortOrder) {
@@ -213,4 +172,4 @@
             }
         };
     }
-}
+}
\ 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
new file mode 100644
index 0000000..97e1e38
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ExportTestResolver.java
@@ -0,0 +1,39 @@
+/*
+ * 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.mock.MockContentResolver;
+
+import junit.framework.TestCase;
+
+/* package */ class ExportTestResolver extends MockContentResolver {
+    private final ExportTestProvider mProvider;
+    public ExportTestResolver(TestCase testCase) {
+        mProvider = new ExportTestProvider(testCase);
+        addProvider(VCardComposer.VCARD_TEST_AUTHORITY, mProvider);
+        addProvider(RawContacts.CONTENT_URI.getAuthority(), mProvider);
+    }
+
+    public ContactEntry addInputContactEntry() {
+        return mProvider.buildInputEntry();
+    }
+
+    public ExportTestProvider getProvider() {
+        return mProvider;
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestProvider.java
similarity index 91%
rename from core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
rename to core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestProvider.java
index c3f6f79..ffbf95d 100644
--- a/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.pim.vcard;
+package android.pim.vcard.test_utils;
 
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
@@ -34,7 +34,7 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.test.mock.MockContentResolver;
+import android.test.mock.MockContentProvider;
 import android.text.TextUtils;
 
 import junit.framework.TestCase;
@@ -50,37 +50,6 @@
 import java.util.TreeMap;
 import java.util.Map.Entry;
 
-/* package */ class ImportTestResolver extends MockContentResolver {
-    final ImportTestProvider mProvider;
-
-    public ImportTestResolver(TestCase testCase) {
-        mProvider = new ImportTestProvider(testCase);
-    }
-
-    @Override
-    public ContentProviderResult[] applyBatch(String authority,
-            ArrayList<ContentProviderOperation> operations) {
-        equalsString(authority, RawContacts.CONTENT_URI.toString());
-        return mProvider.applyBatch(operations);
-    }
-
-    public void addExpectedContentValues(ContentValues expectedContentValues) {
-        mProvider.addExpectedContentValues(expectedContentValues);
-    }
-
-    public void verify() {
-        mProvider.verify();
-    }
-
-    private static boolean equalsString(String a, String b) {
-        if (a == null || a.length() == 0) {
-            return b == null || b.length() == 0;
-        } else {
-            return a.equals(b);
-        }
-    }
-}
-
 /* package */ class ImportTestProvider extends MockContentProvider {
     private static final Set<String> sKnownMimeTypeSet =
         new HashSet<String>(Arrays.asList(StructuredName.CONTENT_ITEM_TYPE,
@@ -296,4 +265,4 @@
         }
         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
new file mode 100644
index 0000000..29738c6
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestResolver.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package 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.mock.MockContentResolver;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+
+/* package */ class ImportTestResolver extends MockContentResolver {
+    private final ImportTestProvider mProvider;
+
+    public ImportTestResolver(TestCase testCase) {
+        mProvider = new ImportTestProvider(testCase);
+    }
+
+    @Override
+    public ContentProviderResult[] applyBatch(String authority,
+            ArrayList<ContentProviderOperation> operations) {
+        equalsString(authority, RawContacts.CONTENT_URI.toString());
+        return mProvider.applyBatch(operations);
+    }
+
+    public void addExpectedContentValues(ContentValues expectedContentValues) {
+        mProvider.addExpectedContentValues(expectedContentValues);
+    }
+
+    public void verify() {
+        mProvider.verify();
+    }
+
+    private static boolean equalsString(String a, String b) {
+        if (a == null || a.length() == 0) {
+            return b == null || b.length() == 0;
+        } else {
+            return a.equals(b);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/LineVerifier.java b/core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifier.java
similarity index 94%
rename from core/tests/coretests/src/android/pim/vcard/LineVerifier.java
rename to core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifier.java
index cef15fd..3edec67 100644
--- a/core/tests/coretests/src/android/pim/vcard/LineVerifier.java
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifier.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.pim.vcard;
+package android.pim.vcard.test_utils;
 
 import android.content.Context;
 import android.pim.vcard.VCardComposer;
@@ -22,7 +22,7 @@
 
 import java.util.ArrayList;
 
-class LineVerifier implements VCardComposer.OneEntryHandler {
+public class LineVerifier implements VCardComposer.OneEntryHandler {
     private final TestCase mTestCase;
     private final ArrayList<LineVerifierElem> mLineVerifierElemList;
     private int mVCardType;
diff --git a/core/tests/coretests/src/android/pim/vcard/LineVerifierElem.java b/core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifierElem.java
similarity index 97%
rename from core/tests/coretests/src/android/pim/vcard/LineVerifierElem.java
rename to core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifierElem.java
index b23b29b..4f7a9cf 100644
--- a/core/tests/coretests/src/android/pim/vcard/LineVerifierElem.java
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifierElem.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package android.pim.vcard;
+package android.pim.vcard.test_utils;
 
 import android.pim.vcard.VCardConfig;
 import android.text.TextUtils;
@@ -23,7 +23,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-class LineVerifierElem {
+public class LineVerifierElem {
     private final TestCase mTestCase;
     private final List<String> mExpectedLineList = new ArrayList<String>();
     private final boolean mIsV30;
diff --git a/core/tests/coretests/src/android/pim/vcard/PropertyNode.java b/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNode.java
similarity index 95%
rename from core/tests/coretests/src/android/pim/vcard/PropertyNode.java
rename to core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNode.java
index 2c1f6d2..de7ad8e 100644
--- a/core/tests/coretests/src/android/pim/vcard/PropertyNode.java
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNode.java
@@ -13,11 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.pim.vcard;
+package android.pim.vcard.test_utils;
 
 import android.content.ContentValues;
 import android.pim.vcard.VCardEntry;
-import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -26,12 +25,18 @@
 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
- * ContactStruct is wrong withouth this class.
+ * {@link VCardEntry} is wrong without this class.
+ * </p>
  */
 public class PropertyNode {
     public String propName;
@@ -43,7 +48,8 @@
      */
     public byte[] propValue_bytes;
 
-    /** param store: key=paramType, value=paramValue
+    /**
+     * 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"]...
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
new file mode 100644
index 0000000..5c8215f
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNodesVerifier.java
@@ -0,0 +1,90 @@
+/*
+ * 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.pim.vcard.VCardParser;
+import android.pim.vcard.VCardParser_V21;
+import android.pim.vcard.VCardParser_V30;
+import android.pim.vcard.exception.VCardException;
+import android.test.AndroidTestCase;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+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 vCardParser)
+            throws IOException, VCardException {
+        verify(mAndroidTestCase.getContext().getResources().openRawResource(resId),
+                vCardType, vCardParser);
+    }
+
+    public void verify(InputStream is, int vCardType) throws IOException, VCardException {
+        final VCardParser vCardParser;
+        if (VCardConfig.isV30(vCardType)) {
+            vCardParser = new VCardParser_V30();
+        } else {
+            vCardParser = new VCardParser_V21();
+        }
+        verify(is, vCardType, vCardParser);
+    }
+
+    public void verify(InputStream is, int vCardType, final VCardParser vCardParser)
+            throws IOException, VCardException {
+        try {
+            vCardParser.parse(is, this);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    @Override
+    public void endEntry() {
+        super.endEntry();
+        mAndroidTestCase.assertTrue(mIndex < mPropertyNodesVerifierElemList.size());
+        mAndroidTestCase.assertTrue(mIndex < vNodeList.size());
+        mPropertyNodesVerifierElemList.get(mIndex).verify(vNodeList.get(mIndex));
+        mIndex++;
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/PropertyNodesVerifier.java b/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNodesVerifierElem.java
similarity index 82%
rename from core/tests/coretests/src/android/pim/vcard/PropertyNodesVerifier.java
rename to core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNodesVerifierElem.java
index cfdd074..8c6c734 100644
--- a/core/tests/coretests/src/android/pim/vcard/PropertyNodesVerifier.java
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNodesVerifierElem.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 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.
@@ -13,87 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.pim.vcard;
+package android.pim.vcard.test_utils;
 
 import android.content.ContentValues;
-import android.pim.vcard.VCardConfig;
-import android.pim.vcard.VCardParser;
-import android.pim.vcard.VCardParser_V21;
-import android.pim.vcard.VCardParser_V30;
-import android.pim.vcard.exception.VCardException;
-import android.test.AndroidTestCase;
 
 import junit.framework.TestCase;
 
-import java.io.IOException;
-import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 
-/* package */ class PropertyNodesVerifier extends VNodeBuilder {
-    private final List<PropertyNodesVerifierElem> mPropertyNodesVerifierElemList;
-    private final AndroidTestCase mAndroidTestCase;
-    private int mIndex;
-
-    public PropertyNodesVerifier(AndroidTestCase testCase) {
-        mPropertyNodesVerifierElemList = new ArrayList<PropertyNodesVerifierElem>();
-        mAndroidTestCase = testCase;
-    }
-
-    public PropertyNodesVerifierElem addPropertyNodesVerifierElem() {
-        PropertyNodesVerifierElem elem = new PropertyNodesVerifierElem(mAndroidTestCase);
-        mPropertyNodesVerifierElemList.add(elem);
-        return elem;
-    }
-
-    public void verify(int resId, int vCardType)
-            throws IOException, VCardException {
-        verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), vCardType);
-    }
-
-    public void verify(int resId, int vCardType, final VCardParser vCardParser)
-            throws IOException, VCardException {
-        verify(mAndroidTestCase.getContext().getResources().openRawResource(resId),
-                vCardType, vCardParser);
-    }
-
-    public void verify(InputStream is, int vCardType) throws IOException, VCardException {
-        final VCardParser vCardParser;
-        if (VCardConfig.isV30(vCardType)) {
-            vCardParser = new VCardParser_V30(true);  // Use StrictParsing.
-        } else {
-            vCardParser = new VCardParser_V21();
-        }
-        verify(is, vCardType, vCardParser);
-    }
-
-    public void verify(InputStream is, int vCardType, final VCardParser vCardParser)
-            throws IOException, VCardException {
-        try {
-            vCardParser.parse(is, this);
-        } finally {
-            if (is != null) {
-                try {
-                    is.close();
-                } catch (IOException e) {
-                }
-            }
-        }
-    }
-
-    @Override
-    public void endEntry() {
-        super.endEntry();
-        mAndroidTestCase.assertTrue(mIndex < mPropertyNodesVerifierElemList.size());
-        mAndroidTestCase.assertTrue(mIndex < vNodeList.size());
-        mPropertyNodesVerifierElemList.get(mIndex).verify(vNodeList.get(mIndex));
-        mIndex++;
-    }
-}
-
 /**
  * Utility class which verifies input VNode.
  *
@@ -102,7 +33,7 @@
  * If the node does not exist in the "ordered list", the class refers to
  * "unorderd expected property set" and checks the node is expected somewhere.
  */
-/* package */ class PropertyNodesVerifierElem {
+public class PropertyNodesVerifierElem {
     public static class TypeSet extends HashSet<String> {
         public TypeSet(String ... array) {
             super(Arrays.asList(array));
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java b/core/tests/coretests/src/android/pim/vcard/test_utils/VCardVerifier.java
similarity index 79%
rename from core/tests/coretests/src/android/pim/vcard/VCardVerifier.java
rename to core/tests/coretests/src/android/pim/vcard/test_utils/VCardVerifier.java
index bfc3158..5fb2942 100644
--- a/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/VCardVerifier.java
@@ -13,9 +13,8 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package android.pim.vcard;
+package android.pim.vcard.test_utils;
 
-import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.EntityIterator;
@@ -31,6 +30,8 @@
 import android.pim.vcard.exception.VCardException;
 import android.test.AndroidTestCase;
 import android.test.mock.MockContext;
+import android.text.TextUtils;
+import android.util.Log;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -38,19 +39,32 @@
 import java.lang.reflect.Method;
 import java.util.Arrays;
 
-/* package */ class CustomMockContext extends MockContext {
-    final ContentResolver mResolver;
-    public CustomMockContext(ContentResolver resolver) {
-        mResolver = resolver;
+/**
+ * <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;
+        }
     }
 
-    @Override
-    public ContentResolver getContentResolver() {
-        return mResolver;
-    }
-}
-
-/* package */ class VCardVerifier {
     private class VCardVerifierInternal implements VCardComposer.OneEntryHandler {
         public boolean onInit(Context context) {
             return true;
@@ -80,9 +94,11 @@
     private ContentValuesVerifier mContentValuesVerifier;
     private boolean mInitialized;
     private boolean mVerified = false;
+    private String mCharset;
 
-    public VCardVerifier(AndroidTestCase androidTestCase) {
-        mTestCase = androidTestCase;
+    // Called by VCardTestsBase
+    public VCardVerifier(AndroidTestCase testCase) {
+        mTestCase = testCase;
         mVCardVerifierInternal = new VCardVerifierInternal();
         mExportTestResolver = null;
         mInputStream = null;
@@ -90,17 +106,7 @@
         mVerified = false;
     }
 
-    public void initForExportTest(int vcardType) {
-        if (mInitialized) {
-            mTestCase.fail("Already initialized");
-        }
-        mExportTestResolver = new ExportTestResolver(mTestCase);
-        mVCardType = vcardType;
-        mIsV30 = VCardConfig.isV30(vcardType);
-        mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
-        mInitialized = true;
-    }
-
+    // Should be called at the beginning of each import test.
     public void initForImportTest(int vcardType, int resId) {
         if (mInitialized) {
             mTestCase.fail("Already initialized");
@@ -112,6 +118,27 @@
         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) {
+            mTestCase.fail("Already initialized");
+        }
+        mExportTestResolver = new ExportTestResolver(mTestCase);
+        mVCardType = vcardType;
+        mIsV30 = VCardConfig.isV30(vcardType);
+        mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
+        mInitialized = true;
+        if (TextUtils.isEmpty(charset)) {
+            mCharset = "UTF-8";
+        } else {
+            mCharset = charset;
+        }
+    }
+
     private void setInputResourceId(int resId) {
         InputStream inputStream = mTestCase.getContext().getResources().openRawResource(resId);
         if (inputStream == null) {
@@ -188,7 +215,7 @@
     }
 
     private void verifyOneVCard(final String vcard) {
-        // Log.d("@@@", vcard);
+        Log.d(LOG_TAG, vcard);
         final VCardInterpreter builder;
         if (mContentValuesVerifier != null) {
             final VNodeBuilder vnodeBuilder = mPropertyNodesVerifier;
@@ -209,14 +236,15 @@
             }
         }
 
-        final VCardParser parser =
-                (mIsV30 ? new VCardParser_V30(true) : new VCardParser_V21());
         InputStream is = null;
         try {
-            String charset =
-                (VCardConfig.usesShiftJis(mVCardType) ? "SHIFT_JIS" : "UTF-8");
-            is = new ByteArrayInputStream(vcard.getBytes(charset));
-            mTestCase.assertEquals(true, parser.parse(is, null, builder));
+            // 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 = (mIsV30 ?
+                    new VCardParser_V30(mVCardType) : new VCardParser_V21(mVCardType));
+            is = new ByteArrayInputStream(vcard.getBytes(mCharset));
+            parser.parse(is, builder);
         } catch (IOException e) {
             mTestCase.fail("Unexpected IOException: " + e.getMessage());
         } catch (VCardException e) {
@@ -226,6 +254,7 @@
                 try {
                     is.close();
                 } catch (IOException e) {
+                    mTestCase.fail("Unexpected IOException: " + e.getMessage());
                 }
             }
         }
@@ -267,10 +296,13 @@
             final ContentResolver resolver,
             final Uri uri, final String selection,
             final String[] selectionArgs, final String sortOrder) {
-        final ContentProvider provider =
-            resolver.acquireContentProviderClient(uri).getLocalContentProvider();
-        return ((ExportTestProvider)provider).queryEntities(
-                uri, selection, selectionArgs, sortOrder);
+        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()
@@ -281,7 +313,7 @@
 
     private void verifyForExportTest() {
        final VCardComposer composer =
-            new VCardComposer(new CustomMockContext(mExportTestResolver), mVCardType);
+            new VCardComposer(new CustomMockContext(mExportTestResolver), mVCardType, mCharset);
         composer.addHandler(mLineVerifier);
         composer.addHandler(mVCardVerifierInternal);
         if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) {
@@ -292,8 +324,8 @@
             while (!composer.isAfterLast()) {
                 try {
                     final Method mockGetEntityIteratorMethod = getMockGetEntityIteratorMethod();
-                    mTestCase.assertTrue(
-                            composer.createOneEntry(getMockGetEntityIteratorMethod()));
+                    mTestCase.assertNotNull(mockGetEntityIteratorMethod);
+                    mTestCase.assertTrue(composer.createOneEntry(mockGetEntityIteratorMethod));
                 } catch (Exception e) {
                     e.printStackTrace();
                     mTestCase.fail();
diff --git a/core/tests/coretests/src/android/pim/vcard/VNode.java b/core/tests/coretests/src/android/pim/vcard/test_utils/VNode.java
similarity index 95%
rename from core/tests/coretests/src/android/pim/vcard/VNode.java
rename to core/tests/coretests/src/android/pim/vcard/test_utils/VNode.java
index 79f10dc..b890e2c 100644
--- a/core/tests/coretests/src/android/pim/vcard/VNode.java
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/VNode.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.pim.vcard;
+package android.pim.vcard.test_utils;
 
 import java.util.ArrayList;
 
diff --git a/core/tests/coretests/src/android/pim/vcard/VNodeBuilder.java b/core/tests/coretests/src/android/pim/vcard/test_utils/VNodeBuilder.java
similarity index 66%
rename from core/tests/coretests/src/android/pim/vcard/VNodeBuilder.java
rename to core/tests/coretests/src/android/pim/vcard/test_utils/VNodeBuilder.java
index 0e6c325..25fb6ac 100644
--- a/core/tests/coretests/src/android/pim/vcard/VNodeBuilder.java
+++ b/core/tests/coretests/src/android/pim/vcard/test_utils/VNodeBuilder.java
@@ -13,17 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.pim.vcard;
+package android.pim.vcard.test_utils;
 
 import android.content.ContentValues;
-import android.pim.vcard.VCardInterpreter;
 import android.pim.vcard.VCardConfig;
+import android.pim.vcard.VCardInterpreter;
+import android.pim.vcard.VCardUtils;
 import android.util.CharsetUtils;
 import android.util.Log;
 
-import org.apache.commons.codec.DecoderException;
 import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.codec.net.QuotedPrintableCodec;
 
 import java.io.UnsupportedEncodingException;
 import java.nio.ByteBuffer;
@@ -32,34 +31,29 @@
 import java.util.List;
 
 /**
- * Store the parse result to custom datastruct: VNode, PropertyNode
+ * <p>
+ * The class storing the parse result to custom datastruct:
+ * {@link VNode}, and {@link PropertyNode}.
  * Maybe several vcard instance, so use vNodeList to store.
- * VNode: standy by a vcard instance.
- * PropertyNode: standy by a property line of a card.
- *
- * Previously used in main vCard handling code but now exists only for testing.
+ * </p>
+ * <p>
+ * This is called VNode, not VCardNode, since it was used for expressing vCalendar (iCal).
+ * </p>
  */
-public class VNodeBuilder implements VCardInterpreter {
+/* package */ class VNodeBuilder implements VCardInterpreter {
     static private String LOG_TAG = "VNodeBuilder"; 
     
-    /**
-     * If there's no other information available, this class uses this charset for encoding
-     * byte arrays.
-     */
-    static public String TARGET_CHARSET = "UTF-8"; 
-    
-    /** type=VNode */
     public List<VNode> vNodeList = new ArrayList<VNode>();
     private int mNodeListPos = 0;
     private VNode mCurrentVNode;
     private PropertyNode mCurrentPropNode;
     private String mCurrentParamType;
-    
+
     /**
      * The charset using which VParser parses the text.
      */
     private String mSourceCharset;
-    
+
     /**
      * The charset with which byte array is encoded to String.
      */
@@ -68,11 +62,11 @@
     private boolean mStrictLineBreakParsing;
     
     public VNodeBuilder() {
-        this(VCardConfig.DEFAULT_CHARSET, TARGET_CHARSET, false);
+        this(VCardConfig.DEFAULT_INTERMEDIATE_CHARSET, VCardConfig.DEFAULT_IMPORT_CHARSET, false);
     }
 
-    public VNodeBuilder(String charset, boolean strictLineBreakParsing) {
-        this(null, charset, strictLineBreakParsing);
+    public VNodeBuilder(String targetCharset, boolean strictLineBreakParsing) {
+        this(null, targetCharset, strictLineBreakParsing);
     }
     
     /**
@@ -83,12 +77,12 @@
         if (sourceCharset != null) {
             mSourceCharset = sourceCharset;
         } else {
-            mSourceCharset = VCardConfig.DEFAULT_CHARSET;
+            mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
         }
         if (targetCharset != null) {
             mTargetCharset = targetCharset;
         } else {
-            mTargetCharset = TARGET_CHARSET;
+            mTargetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET;
         }
         mStrictLineBreakParsing = strictLineBreakParsing;
     }
@@ -149,7 +143,6 @@
         mCurrentPropNode.propName = name;
     }
 
-    // Used only in VCard.
     public void propertyGroup(String group) {
         mCurrentPropNode.propGroupSet.add(group);
     }
@@ -196,67 +189,8 @@
                     Base64.decodeBase64(value.getBytes());
                 return value;
             } else if (encoding.equals("QUOTED-PRINTABLE")) {
-                String quotedPrintable = value
-                .replaceAll("= ", " ").replaceAll("=\t", "\t");
-                String[] lines;
-                if (mStrictLineBreakParsing) {
-                    lines = quotedPrintable.split("\r\n");
-                } else {
-                    StringBuilder builder = new StringBuilder();
-                    int length = quotedPrintable.length();
-                    ArrayList<String> list = new ArrayList<String>();
-                    for (int i = 0; i < length; i++) {
-                        char ch = quotedPrintable.charAt(i);
-                        if (ch == '\n') {
-                            list.add(builder.toString());
-                            builder = new StringBuilder();
-                        } else if (ch == '\r') {
-                            list.add(builder.toString());
-                            builder = new StringBuilder();
-                            if (i < length - 1) {
-                                char nextCh = quotedPrintable.charAt(i + 1);
-                                if (nextCh == '\n') {
-                                    i++;
-                                }
-                            }
-                        } else {
-                            builder.append(ch);
-                        }
-                    }
-                    String finalLine = builder.toString();
-                    if (finalLine.length() > 0) {
-                        list.add(finalLine);
-                    }
-                    lines = list.toArray(new String[0]);
-                }
-                StringBuilder builder = new StringBuilder();
-                for (String line : lines) {
-                    if (line.endsWith("=")) {
-                        line = line.substring(0, line.length() - 1);
-                    }
-                    builder.append(line);
-                }
-                byte[] bytes;
-                try {
-                    bytes = builder.toString().getBytes(mSourceCharset);
-                } catch (UnsupportedEncodingException e1) {
-                    Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset);
-                    bytes = builder.toString().getBytes();
-                }
-                
-                try {
-                    bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes);
-                } catch (DecoderException e) {
-                    Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e);
-                    return "";
-                }
-
-                try {
-                    return new String(bytes, targetCharset);
-                } catch (UnsupportedEncodingException e) {
-                    Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
-                    return new String(bytes);
-                }
+                return VCardUtils.parseQuotedPrintable(
+                        value, mStrictLineBreakParsing, mSourceCharset, targetCharset);
             }
             // Unknown encoding. Fall back to default.
         }
@@ -309,6 +243,6 @@
     }
     
     public String getResult(){
-        return null;
+        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/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java
index 957c593..b90c97b 100644
--- a/core/tests/coretests/src/android/util/PatternsTest.java
+++ b/core/tests/coretests/src/android/util/PatternsTest.java
@@ -74,6 +74,10 @@
         t = Patterns.WEB_URL.matcher("\uD604\uAE08\uC601\uC218\uC99D.kr").matches();
         assertTrue("Valid URL", t);
 
+        t = Patterns.WEB_URL.matcher("http://brainstormtech.blogs.fortune.cnn.com/2010/03/11/" +
+            "top-five-moments-from-eric-schmidt\u2019s-talk-in-abu-dhabi/").matches();
+        assertTrue("Valid URL", t);
+
         t = Patterns.WEB_URL.matcher("ftp://www.example.com").matches();
         assertFalse("Matched invalid protocol", t);
 
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 64a0fff..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));
-    }
-
-    @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/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/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 3e3f87b..183c896 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -93,7 +93,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);
         
diff --git a/graphics/java/android/graphics/PorterDuff.java b/graphics/java/android/graphics/PorterDuff.java
index 3904234..0e405c2 100644
--- a/graphics/java/android/graphics/PorterDuff.java
+++ b/graphics/java/android/graphics/PorterDuff.java
@@ -53,7 +53,10 @@
         /** [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;
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 6a7b2d1..4c1d243 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
@@ -645,6 +654,8 @@
      * Calling this method on a mutable Drawable will have no effect.
      *
      * @return This drawable.
+     * @see ConstantState
+     * @see #getConstantState()
      */
     public Drawable mutate() {
         return this;
@@ -750,6 +761,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")) {
@@ -771,7 +784,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());
             }
@@ -806,6 +819,9 @@
         return null;
     }
 
+    /**
+     * Inflate this Drawable from an XML resource.
+     */
     public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
             throws XmlPullParserException, IOException {
 
@@ -814,6 +830,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 {
@@ -821,12 +843,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();
         /**
@@ -845,6 +882,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..124d907 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -17,8 +17,16 @@
 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;
 
+/**
+ * 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 {
 
     /**
@@ -196,8 +204,7 @@
                 mDrawableContainerState.getOpacity();
     }
 
-    public boolean selectDrawable(int idx)
-    {
+    public boolean selectDrawable(int idx) {
         if (idx == mCurIndex) {
             return false;
         }
@@ -255,6 +262,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;
 
@@ -443,12 +456,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];
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/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 17c0778..d32a0b5 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -76,10 +76,30 @@
         subData1D(0, mType.getElementCount(), d);
     }
 
+    public void subData(int off, 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(off, count, data.length, data.length);
+        mRS.nAllocationSubData1D(mID, off, count, 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.");
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Byte2.java
similarity index 89%
copy from graphics/java/android/renderscript/Vector2f.java
copy to graphics/java/android/renderscript/Byte2.java
index 567d57fa..95cf88c 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/graphics/java/android/renderscript/Byte2.java
@@ -24,12 +24,12 @@
  * @hide
  *
  **/
-public class Vector2f {
-    public Vector2f() {
+public class Byte2 {
+    public Byte2() {
     }
 
-    public float x;
-    public float y;
+    public byte x;
+    public byte y;
 }
 
 
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Byte3.java
similarity index 87%
copy from graphics/java/android/renderscript/Vector2f.java
copy to graphics/java/android/renderscript/Byte3.java
index 567d57fa..a6c0ca9 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/graphics/java/android/renderscript/Byte3.java
@@ -24,12 +24,13 @@
  * @hide
  *
  **/
-public class Vector2f {
-    public Vector2f() {
+public class Byte3 {
+    public Byte3() {
     }
 
-    public float x;
-    public float y;
+    public byte x;
+    public byte y;
+    public byte z;
 }
 
 
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Byte4.java
similarity index 85%
copy from graphics/java/android/renderscript/Vector2f.java
copy to graphics/java/android/renderscript/Byte4.java
index 567d57fa..a5bfc61 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/graphics/java/android/renderscript/Byte4.java
@@ -24,14 +24,15 @@
  * @hide
  *
  **/
-public class Vector2f {
-    public Vector2f() {
+public class Byte4 {
+    public Byte4() {
     }
 
-    public float x;
-    public float y;
+    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..7b155fe 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -126,6 +126,77 @@
         return rs.mElement_USER_F32;
     }
 
+    public static Element USER_ELEMENT(RenderScript rs) {
+        if(rs.mElement_USER_ELEMENT == null) {
+            rs.mElement_USER_ELEMENT = createUser(rs, DataType.RS_ELEMENT);
+        }
+        return rs.mElement_USER_ELEMENT;
+    }
+
+    public static Element USER_TYPE(RenderScript rs) {
+        if(rs.mElement_USER_TYPE == null) {
+            rs.mElement_USER_TYPE = createUser(rs, DataType.RS_TYPE);
+        }
+        return rs.mElement_USER_TYPE;
+    }
+
+    public static Element USER_ALLOCATION(RenderScript rs) {
+        if(rs.mElement_USER_ALLOCATION == null) {
+            rs.mElement_USER_ALLOCATION = createUser(rs, DataType.RS_ALLOCATION);
+        }
+        return rs.mElement_USER_ALLOCATION;
+    }
+
+    public static Element USER_SAMPLER(RenderScript rs) {
+        if(rs.mElement_USER_SAMPLER == null) {
+            rs.mElement_USER_SAMPLER = createUser(rs, DataType.RS_SAMPLER);
+        }
+        return rs.mElement_USER_SAMPLER;
+    }
+
+    public static Element USER_SCRIPT(RenderScript rs) {
+        if(rs.mElement_USER_SCRIPT == null) {
+            rs.mElement_USER_SCRIPT = createUser(rs, DataType.RS_SCRIPT);
+        }
+        return rs.mElement_USER_SCRIPT;
+    }
+
+    public static Element USER_MESH(RenderScript rs) {
+        if(rs.mElement_USER_MESH == null) {
+            rs.mElement_USER_MESH = createUser(rs, DataType.RS_MESH);
+        }
+        return rs.mElement_USER_MESH;
+    }
+
+    public static Element USER_PROGRAM_FRAGMENT(RenderScript rs) {
+        if(rs.mElement_USER_PROGRAM_FRAGMENT == null) {
+            rs.mElement_USER_PROGRAM_FRAGMENT = createUser(rs, DataType.RS_PROGRAM_FRAGMENT);
+        }
+        return rs.mElement_USER_PROGRAM_FRAGMENT;
+    }
+
+    public static Element USER_PROGRAM_VERTEX(RenderScript rs) {
+        if(rs.mElement_USER_PROGRAM_VERTEX == null) {
+            rs.mElement_USER_PROGRAM_VERTEX = createUser(rs, DataType.RS_PROGRAM_VERTEX);
+        }
+        return rs.mElement_USER_PROGRAM_VERTEX;
+    }
+
+    public static Element USER_PROGRAM_RASTER(RenderScript rs) {
+        if(rs.mElement_USER_PROGRAM_RASTER == null) {
+            rs.mElement_USER_PROGRAM_RASTER = createUser(rs, DataType.RS_PROGRAM_RASTER);
+        }
+        return rs.mElement_USER_PROGRAM_RASTER;
+    }
+
+    public static Element USER_PROGRAM_STORE(RenderScript rs) {
+        if(rs.mElement_USER_PROGRAM_STORE == null) {
+            rs.mElement_USER_PROGRAM_STORE = createUser(rs, DataType.RS_PROGRAM_STORE);
+        }
+        return rs.mElement_USER_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);
diff --git a/graphics/java/android/renderscript/FieldPacker.java b/graphics/java/android/renderscript/FieldPacker.java
index b26e47d..6d55c7e 100644
--- a/graphics/java/android/renderscript/FieldPacker.java
+++ b/graphics/java/android/renderscript/FieldPacker.java
@@ -33,21 +33,24 @@
         }
     }
 
-    void reset() {
+    public void reset() {
         mPos = 0;
     }
+    public void reset(int i) {
+        mPos = i;
+    }
 
-    void addI8(byte v) {
+    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 +58,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,14 +70,14 @@
         mData[mPos++] = (byte)((v >> 56) & 0xff);
     }
 
-    void addU8(short v) {
+    public void addU8(short v) {
         if ((v < 0) || (v > 0xff)) {
             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)) {
             throw new IllegalArgumentException("Saving value out of range for type");
         }
@@ -83,7 +86,7 @@
         mData[mPos++] = (byte)(v >> 8);
     }
 
-    void addU32(long v) {
+    public void addU32(long v) {
         if ((v < 0) || (v > 0xffffffff)) {
             throw new IllegalArgumentException("Saving value out of range for type");
         }
@@ -94,7 +97,7 @@
         mData[mPos++] = (byte)((v >> 24) & 0xff);
     }
 
-    void addU64(long v) {
+    public void addU64(long v) {
         if (v < 0) {
             throw new IllegalArgumentException("Saving value out of range for type");
         }
@@ -109,15 +112,135 @@
         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(float 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 final byte[] getData() {
         return mData;
     }
 
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Float2.java
similarity index 93%
rename from graphics/java/android/renderscript/Vector2f.java
rename to graphics/java/android/renderscript/Float2.java
index 567d57fa..8fea91f 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/graphics/java/android/renderscript/Float2.java
@@ -24,8 +24,8 @@
  * @hide
  *
  **/
-public class Vector2f {
-    public Vector2f() {
+public class Float2 {
+    public Float2() {
     }
 
     public float x;
diff --git a/graphics/java/android/renderscript/Vector3f.java b/graphics/java/android/renderscript/Float3.java
similarity index 94%
rename from graphics/java/android/renderscript/Vector3f.java
rename to graphics/java/android/renderscript/Float3.java
index f2842f3..9d9e406 100644
--- a/graphics/java/android/renderscript/Vector3f.java
+++ b/graphics/java/android/renderscript/Float3.java
@@ -24,8 +24,8 @@
  * @hide
  *
  **/
-public class Vector3f {
-    public Vector3f() {
+public class Float3 {
+    public Float3() {
     }
 
     public float x;
diff --git a/graphics/java/android/renderscript/Vector4f.java b/graphics/java/android/renderscript/Float4.java
similarity index 94%
rename from graphics/java/android/renderscript/Vector4f.java
rename to graphics/java/android/renderscript/Float4.java
index fabd959..a703e80 100644
--- a/graphics/java/android/renderscript/Vector4f.java
+++ b/graphics/java/android/renderscript/Float4.java
@@ -24,8 +24,8 @@
  * @hide
  *
  **/
-public class Vector4f {
-    public Vector4f() {
+public class Float4 {
+    public Float4() {
     }
 
     public float x;
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Int2.java
similarity index 89%
copy from graphics/java/android/renderscript/Vector2f.java
copy to graphics/java/android/renderscript/Int2.java
index 567d57fa..56e2fe9 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/graphics/java/android/renderscript/Int2.java
@@ -24,12 +24,12 @@
  * @hide
  *
  **/
-public class Vector2f {
-    public Vector2f() {
+public class Int2 {
+    public Int2() {
     }
 
-    public float x;
-    public float y;
+    public int x;
+    public int y;
 }
 
 
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Int3.java
similarity index 88%
copy from graphics/java/android/renderscript/Vector2f.java
copy to graphics/java/android/renderscript/Int3.java
index 567d57fa..1b27509 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/graphics/java/android/renderscript/Int3.java
@@ -24,12 +24,13 @@
  * @hide
  *
  **/
-public class Vector2f {
-    public Vector2f() {
+public class Int3 {
+    public Int3() {
     }
 
-    public float x;
-    public float y;
+    public int x;
+    public int y;
+    public int z;
 }
 
 
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Int4.java
similarity index 86%
copy from graphics/java/android/renderscript/Vector2f.java
copy to graphics/java/android/renderscript/Int4.java
index 567d57fa..3d6f3f5 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/graphics/java/android/renderscript/Int4.java
@@ -24,14 +24,15 @@
  * @hide
  *
  **/
-public class Vector2f {
-    public Vector2f() {
+public class Int4 {
+    public Int4() {
     }
 
-    public float x;
-    public float y;
+    public int x;
+    public int y;
+    public int z;
+    public int w;
 }
 
 
 
-
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Long2.java
similarity index 89%
copy from graphics/java/android/renderscript/Vector2f.java
copy to graphics/java/android/renderscript/Long2.java
index 567d57fa..11ead2f 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/graphics/java/android/renderscript/Long2.java
@@ -24,12 +24,12 @@
  * @hide
  *
  **/
-public class Vector2f {
-    public Vector2f() {
+public class Long2 {
+    public Long2() {
     }
 
-    public float x;
-    public float y;
+    public long x;
+    public long y;
 }
 
 
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Long3.java
similarity index 87%
copy from graphics/java/android/renderscript/Vector2f.java
copy to graphics/java/android/renderscript/Long3.java
index 567d57fa..1604532 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/graphics/java/android/renderscript/Long3.java
@@ -24,12 +24,13 @@
  * @hide
  *
  **/
-public class Vector2f {
-    public Vector2f() {
+public class Long3 {
+    public Long3() {
     }
 
-    public float x;
-    public float y;
+    public long x;
+    public long y;
+    public long z;
 }
 
 
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Long4.java
similarity index 85%
copy from graphics/java/android/renderscript/Vector2f.java
copy to graphics/java/android/renderscript/Long4.java
index 567d57fa..2fd2747 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/graphics/java/android/renderscript/Long4.java
@@ -24,14 +24,15 @@
  * @hide
  *
  **/
-public class Vector2f {
-    public Vector2f() {
+public class Long4 {
+    public Long4() {
     }
 
-    public float x;
-    public float y;
+    public long x;
+    public long y;
+    public long z;
+    public long w;
 }
 
 
 
-
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index a935243..db2a3fd 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -203,6 +203,17 @@
     Element mElement_USER_I32;
     Element mElement_USER_F32;
 
+    Element mElement_USER_ELEMENT;
+    Element mElement_USER_TYPE;
+    Element mElement_USER_ALLOCATION;
+    Element mElement_USER_SAMPLER;
+    Element mElement_USER_SCRIPT;
+    Element mElement_USER_MESH;
+    Element mElement_USER_PROGRAM_FRAGMENT;
+    Element mElement_USER_PROGRAM_VERTEX;
+    Element mElement_USER_PROGRAM_RASTER;
+    Element mElement_USER_PROGRAM_STORE;
+
     Element mElement_A_8;
     Element mElement_RGB_565;
     Element mElement_RGB_888;
diff --git a/graphics/java/android/renderscript/Script.java b/graphics/java/android/renderscript/Script.java
index 57ccfa3..0d21368 100644
--- a/graphics/java/android/renderscript/Script.java
+++ b/graphics/java/android/renderscript/Script.java
@@ -42,6 +42,10 @@
         }
     }
 
+    protected void invoke(int slot) {
+        mRS.nScriptInvoke(mID, slot);
+    }
+
     Script(int id, RenderScript rs) {
         super(rs);
         mID = id;
@@ -145,5 +149,48 @@
 
     }
 
+
+    public static class FieldBase {
+        protected Element mElement;
+        protected Type mType;
+        protected Allocation mAllocation;
+
+        protected void init(RenderScript rs, int dimx) {
+            mAllocation = Allocation.createSized(rs, mElement, dimx);
+            mType = mAllocation.getType();
+        }
+
+        protected FieldBase() {
+        }
+
+        public Element getElement() {
+            return mElement;
+        }
+
+        public Type getType() {
+            return mType;
+        }
+
+        public Allocation getAllocation() {
+            return mAllocation;
+        }
+
+        //@Override
+        public void updateAllocation() {
+        }
+
+
+        //
+        /*
+        public class ScriptField_UserField
+            extends android.renderscript.Script.FieldBase {
+
+            protected
+
+        }
+
+        */
+
+    }
 }
 
diff --git a/graphics/java/android/renderscript/ScriptC.java b/graphics/java/android/renderscript/ScriptC.java
index bb99e23..f5d5b2f 100644
--- a/graphics/java/android/renderscript/ScriptC.java
+++ b/graphics/java/android/renderscript/ScriptC.java
@@ -37,6 +37,47 @@
         super(id, rs);
     }
 
+    protected ScriptC(RenderScript rs, Resources resources, int resourceID, boolean isRoot) {
+        super(0, rs);
+        mID = internalCreate(rs, resources, resourceID, isRoot);
+    }
+
+
+    private static synchronized int internalCreate(RenderScript rs, Resources resources, int resourceID, boolean isRoot) {
+        byte[] pgm;
+        int pgmLength;
+        InputStream is = resources.openRawResource(resourceID);
+        try {
+            try {
+                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;
+                    }
+                    int bytesRead = is.read(pgm, pgmLength, bytesLeft);
+                    if (bytesRead <= 0) {
+                        break;
+                    }
+                    pgmLength += bytesRead;
+                }
+            } finally {
+                is.close();
+            }
+        } catch(IOException e) {
+            throw new Resources.NotFoundException();
+        }
+
+        rs.nScriptCBegin();
+        rs.nScriptCSetScript(pgm, 0, pgmLength);
+        rs.nScriptSetRoot(isRoot);
+        return rs.nScriptCCreate();
+    }
+
     public static class Builder extends Script.Builder {
         byte[] mProgram;
         int mProgramLength;
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Short2.java
similarity index 89%
copy from graphics/java/android/renderscript/Vector2f.java
copy to graphics/java/android/renderscript/Short2.java
index 567d57fa..426801f 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/graphics/java/android/renderscript/Short2.java
@@ -24,12 +24,12 @@
  * @hide
  *
  **/
-public class Vector2f {
-    public Vector2f() {
+public class Short2 {
+    public Short2() {
     }
 
-    public float x;
-    public float y;
+    public short x;
+    public short y;
 }
 
 
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Short3.java
similarity index 87%
copy from graphics/java/android/renderscript/Vector2f.java
copy to graphics/java/android/renderscript/Short3.java
index 567d57fa..7b9c305 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/graphics/java/android/renderscript/Short3.java
@@ -24,12 +24,13 @@
  * @hide
  *
  **/
-public class Vector2f {
-    public Vector2f() {
+public class Short3 {
+    public Short3() {
     }
 
-    public float x;
-    public float y;
+    public short x;
+    public short y;
+    public short z;
 }
 
 
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Short4.java
similarity index 85%
copy from graphics/java/android/renderscript/Vector2f.java
copy to graphics/java/android/renderscript/Short4.java
index 567d57fa..9a474e2 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/graphics/java/android/renderscript/Short4.java
@@ -24,14 +24,15 @@
  * @hide
  *
  **/
-public class Vector2f {
-    public Vector2f() {
+public class Short4 {
+    public Short4() {
     }
 
-    public float x;
-    public float y;
+    public short x;
+    public short y;
+    public short z;
+    public short w;
 }
 
 
 
-
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/libs/rs/Android.mk b/libs/rs/Android.mk
index 98464a0..0b06022 100644
--- a/libs/rs/Android.mk
+++ b/libs/rs/Android.mk
@@ -76,16 +76,18 @@
 LOCAL_SRC_FILES:= \
 	rsAdapter.cpp \
 	rsAllocation.cpp \
+	rsAnimation.cpp \
 	rsComponent.cpp \
 	rsContext.cpp \
 	rsDevice.cpp \
 	rsElement.cpp \
-        rsFileA3D.cpp \
+    rsFileA3D.cpp \
 	rsLight.cpp \
 	rsLocklessFifo.cpp \
 	rsObjectBase.cpp \
 	rsMatrix.cpp \
-        rsMesh.cpp \
+    rsMesh.cpp \
+    rsMutex.cpp \
 	rsNoise.cpp \
 	rsProgram.cpp \
 	rsProgramFragment.cpp \
@@ -96,7 +98,8 @@
 	rsScript.cpp \
 	rsScriptC.cpp \
 	rsScriptC_Lib.cpp \
-        rsShaderCache.cpp \
+    rsShaderCache.cpp \
+	rsSignal.cpp \
 	rsSimpleMesh.cpp \
 	rsThreadIO.cpp \
 	rsType.cpp \
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index d280f50..7415ba9 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -30,6 +30,7 @@
 typedef void * RsAdapter1D;
 typedef void * RsAdapter2D;
 typedef void * RsAllocation;
+typedef void * RsAnimation;
 typedef void * RsContext;
 typedef void * RsDevice;
 typedef void * RsElement;
@@ -205,7 +206,27 @@
 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
 };
 
 #ifndef NO_RS_FUNCS
diff --git a/libs/rs/java/Fountain/res/raw/fountain2.rs b/libs/rs/java/Fountain/res/raw/fountain2.rs
index 3301140..5d36e35 100644
--- a/libs/rs/java/Fountain/res/raw/fountain2.rs
+++ b/libs/rs/java/Fountain/res/raw/fountain2.rs
@@ -1,9 +1,9 @@
 // Fountain test script
 #pragma version(1)
 
-#include "rs_types.rsh"
-#include "rs_math.rsh"
-#include "rs_graphics.rsh"
+#include "../../../../scriptc/rs_types.rsh"
+#include "../../../../scriptc/rs_math.rsh"
+#include "../../../../scriptc/rs_graphics.rsh"
 
 static int newPart = 0;
 
@@ -12,15 +12,15 @@
     int rate;
     int count;
     float r, g, b;
-    rs_allocation partBuffer;
     rs_mesh partMesh;
+    rs_allocation partBuffer;
 } Control_t;
 Control_t *Control;
 
 typedef struct Point_s{
     float2 delta;
-    float2 position;
-    unsigned int color;
+    rs_position2 pos;
+    rs_color4u color;
 } Point_t;
 Point_t *point;
 
@@ -33,8 +33,6 @@
 
     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 |
@@ -42,9 +40,11 @@
         Point_t * np = &p[newPart];
 
         while (rate--) {
-            np->delta = vec2Rand(rMax);
-            np->position.x = x;
-            np->position.y = y;
+            np->delta.x = rand(rMax);
+            np->delta.y = rand(rMax);
+            //np->delta = vec2Rand(rMax);
+            np->pos.x = Control->x;
+            np->pos.y = Control->y;
             np->color = color;
             newPart++;
             np++;
@@ -57,13 +57,13 @@
 
     for (ct=0; ct < count; ct++) {
         float dy = p->delta.y + 0.15f;
-        float posy = p->position.y + dy;
+        float posy = p->pos.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->pos.x += p->delta.x;
+        p->pos.y = posy;
         p++;
     }
 
diff --git a/libs/rs/java/ImageProcessing/res/raw/threshold2.rs b/libs/rs/java/ImageProcessing/res/raw/threshold2.rs
new file mode 100644
index 0000000..9f687b5
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/res/raw/threshold2.rs
@@ -0,0 +1,49 @@
+#pragma version(1)
+
+#include "../../../../scriptc/rs_types.rsh"
+#include "../../../../scriptc/rs_math.rsh"
+#include "../../../../scriptc/rs_graphics.rsh"
+
+typedef struct Params_s{
+    int inHeight;
+    int inWidth;
+    int outHeight;
+    int outWidth;
+    float threshold;
+} Params_t;
+
+Params_t * Params;
+rs_color4u * InPixel;
+rs_color4u * OutPixel;
+
+
+int main() {
+    int t = uptimeMillis();
+
+    rs_color4u *in = InPixel;
+    rs_color4u *out = 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->x +
+                          0.7154f * in->y +
+                          0.0721f * in->z;
+        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);
+    return 0;
+}
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index cb9937c..08aa369 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -480,3 +480,13 @@
 	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/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 4e8278d..e5ff1d7 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -170,6 +170,7 @@
         glGenerateMipmap(GL_TEXTURE_2D);
     }
 
+    rsc->checkError("Allocation::uploadToTexture");
 }
 
 void Allocation::deferedUploadToBufferObject(const Context *rsc)
@@ -201,6 +202,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)
diff --git a/libs/rs/rsAnimation.cpp b/libs/rs/rsAnimation.cpp
new file mode 100644
index 0000000..48c9334
--- /dev/null
+++ b/libs/rs/rsAnimation.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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 "rsAnimation.h"
+
+
+using namespace android;
+using namespace android::renderscript;
+
+/*
+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..b8db661
--- /dev/null
+++ b/libs/rs/rsAnimation.h
@@ -0,0 +1,68 @@
+/*
+ * 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;
+
+
+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/rsContext.cpp b/libs/rs/rsContext.cpp
index d8a9a99..4107229 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -664,8 +664,7 @@
 
 bool Context::objDestroyOOBInit()
 {
-    int status = pthread_mutex_init(&mObjDestroy.mMutex, NULL);
-    if (status) {
+    if (!mObjDestroy.mMutex.init()) {
         LOGE("Context::ObjDestroyOOBInit mutex init failure");
         return false;
     }
@@ -675,9 +674,8 @@
 void Context::objDestroyOOBRun()
 {
     if (mObjDestroy.mNeedToEmpty) {
-        int status = pthread_mutex_lock(&mObjDestroy.mMutex);
-        if (status) {
-            LOGE("Context::ObjDestroyOOBRun: error %i locking for OOBRun.", status);
+        if (!mObjDestroy.mMutex.lock()) {
+            LOGE("Context::ObjDestroyOOBRun: error locking for OOBRun.");
             return;
         }
 
@@ -686,35 +684,25 @@
         }
         mObjDestroy.mDestroyList.clear();
         mObjDestroy.mNeedToEmpty = false;
-
-        status = pthread_mutex_unlock(&mObjDestroy.mMutex);
-        if (status) {
-            LOGE("Context::ObjDestroyOOBRun: error %i unlocking for set condition.", status);
-        }
+        mObjDestroy.mMutex.unlock();
     }
 }
 
 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);
+    if (!mObjDestroy.mMutex.lock()) {
+        LOGE("Context::ObjDestroyOOBRun: error locking for OOBRun.");
         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);
-    }
+    mObjDestroy.mMutex.unlock();
 }
 
 uint32_t Context::getMessageToClient(void *data, size_t *receiveLen, size_t bufferLen, bool wait)
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 82c3687..8e755a9 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -18,6 +18,7 @@
 #define ANDROID_RS_CONTEXT_H
 
 #include "rsUtils.h"
+#include "rsMutex.h"
 
 #include "rsThreadIO.h"
 #include "rsType.h"
@@ -161,7 +162,7 @@
     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;
 
@@ -227,7 +228,7 @@
 
 
     struct ObjDestroyOOB {
-        pthread_mutex_t mMutex;
+        Mutex mMutex;
         Vector<ObjectBase *> mDestroyList;
         bool mNeedToEmpty;
     };
diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp
index c796520..76ca32e 100644
--- a/libs/rs/rsLocklessFifo.cpp
+++ b/libs/rs/rsLocklessFifo.cpp
@@ -17,7 +17,7 @@
 #include "rsLocklessFifo.h"
 
 using namespace android;
-
+using namespace android::renderscript;
 
 LocklessCommandFifo::LocklessCommandFifo()
 {
@@ -128,15 +128,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;
@@ -192,79 +196,3 @@
     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);
-    }
-}
-
diff --git a/libs/rs/rsLocklessFifo.h b/libs/rs/rsLocklessFifo.h
index d0a4356..ae906ca 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,14 +50,14 @@
     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();
 
@@ -88,4 +73,5 @@
 
 
 }
+}
 #endif
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/graphics/java/android/renderscript/Vector2f.java b/libs/rs/rsMutex.h
similarity index 69%
copy from graphics/java/android/renderscript/Vector2f.java
copy to libs/rs/rsMutex.h
index 567d57fa..47725d7 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/libs/rs/rsMutex.h
@@ -14,24 +14,30 @@
  * limitations under the License.
  */
 
-package android.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
+#ifndef ANDROID_RS_MUTEX_H
+#define ANDROID_RS_MUTEX_H
 
 
-/**
- * @hide
- *
- **/
-public class Vector2f {
-    public Vector2f() {
-    }
+#include "rsUtils.h"
 
-    public float x;
-    public float y;
+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/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/graphics/java/android/renderscript/Vector2f.java b/libs/rs/rsSignal.h
similarity index 65%
copy from graphics/java/android/renderscript/Vector2f.java
copy to libs/rs/rsSignal.h
index 567d57fa..2e760f1 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/libs/rs/rsSignal.h
@@ -14,24 +14,33 @@
  * limitations under the License.
  */
 
-package android.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
+#ifndef ANDROID_RS_SIGNAL_H
+#define ANDROID_RS_SIGNAL_H
 
 
-/**
- * @hide
- *
- **/
-public class Vector2f {
-    public Vector2f() {
-    }
+#include "rsUtils.h"
 
-    public float x;
-    public float y;
+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/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/scriptc/rs_geom.rsh b/libs/rs/scriptc/rs_geom.rsh
new file mode 100644
index 0000000..6e9e9fc
--- /dev/null
+++ b/libs/rs/scriptc/rs_geom.rsh
@@ -0,0 +1,26 @@
+
+extern float3 __attribute__((overloadable)) cross(float3, float3);
+extern float4 __attribute__((overloadable)) cross(float4, float4);
+
+//extern float __attribute__((overloadable)) dot(float, float);
+extern float __attribute__((overloadable)) dot(float2, float2);
+extern float __attribute__((overloadable)) dot(float3, float3);
+extern float __attribute__((overloadable)) dot(float4, float4);
+
+//extern float __attribute__((overloadable)) distance(float, float);
+extern float __attribute__((overloadable)) distance(float2, float2);
+extern float __attribute__((overloadable)) distance(float3, float3);
+extern float __attribute__((overloadable)) distance(float4, float4);
+
+//extern float __attribute__((overloadable)) length(float);
+extern float __attribute__((overloadable)) length(float2);
+extern float __attribute__((overloadable)) length(float3);
+extern float __attribute__((overloadable)) length(float4);
+
+extern float2 __attribute__((overloadable)) normalize(float2);
+extern float3 __attribute__((overloadable)) normalize(float3);
+extern float4 __attribute__((overloadable)) normalize(float4);
+
+
+
+
diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh
index 70cd562..0f03732 100644
--- a/libs/rs/scriptc/rs_graphics.rsh
+++ b/libs/rs/scriptc/rs_graphics.rsh
@@ -1,5 +1,7 @@
 
 
+extern float rand(float max);
+
 extern float2 vec2Rand(float len);
 
 extern float3 float3Norm(float3);
diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh
index 613c7ca..fba0f8f 100644
--- a/libs/rs/scriptc/rs_math.rsh
+++ b/libs/rs/scriptc/rs_math.rsh
@@ -1,264 +1,262 @@
 // Float ops
 
 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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);
 
 
 
@@ -268,11 +266,11 @@
 // Int ops
 
 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 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);
 
 
 
diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh
index 4198a74..b710146 100644
--- a/libs/rs/scriptc/rs_types.rsh
+++ b/libs/rs/scriptc/rs_types.rsh
@@ -68,4 +68,29 @@
 typedef int int16 __attribute__((ext_vector_type(16)));
 
 
+// RS_KIND_POSITION
+typedef float rs_position1;
+typedef float2 rs_position2;
+typedef float3 rs_position3;
+typedef float4 rs_position4;
+
+// RS_KIND_COLOR
+typedef float3 rs_color3f;
+typedef float4 rs_color4f;
+typedef uchar4 rs_color4u;
+
+// RS_KIND_NORMAL
+typedef float3 rs_normal;
+
+// RS_KIND_POINT_SIZE
+typedef float rs_point_size;
+
+// RS_KIND_TEXTURE
+typedef float rs_texture_coord1;
+typedef float2 rs_texture_coord2;
+typedef float3 rs_texture_coord3;
+typedef float4 rs_texture_coord4;
+
+// RS_KIND_INDEX
+typedef ushort rs_index;
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 47e2da2..2e3eae0 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1523,4 +1523,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/opengl/tests/testViewport/Android.mk b/opengl/tests/testViewport/Android.mk
new file mode 100644
index 0000000..3da59b5
--- /dev/null
+++ b/opengl/tests/testViewport/Android.mk
@@ -0,0 +1,22 @@
+#########################################################################
+# 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
+
+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..f4a493e
--- /dev/null
+++ b/opengl/tests/testViewport/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?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-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/policy/com/android/internal/policy/impl/LockScreen.java b/policy/com/android/internal/policy/impl/LockScreen.java
index a5ef1fa..b3707b0 100644
--- a/policy/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/com/android/internal/policy/impl/LockScreen.java
@@ -220,7 +220,6 @@
             }
         });
 
-
         setFocusable(true);
         setFocusableInTouchMode(true);
         setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
@@ -518,7 +517,7 @@
                 mScreenLocked.setText("");
 
                 // layout
-                mScreenLocked.setVisibility(View.VISIBLE);
+                mScreenLocked.setVisibility(View.INVISIBLE);
                 mSelector.setVisibility(View.VISIBLE);
                 mEmergencyCallText.setVisibility(View.GONE);
                 break;
@@ -658,7 +657,6 @@
     /** {@inheritDoc} */
     public void onResume() {
         resetStatusInfo(mUpdateMonitor);
-        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
     }
 
     /** {@inheritDoc} */
@@ -676,6 +674,5 @@
     }
 
     public void onPhoneStateChanged(String newState) {
-        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
     }
 }
diff --git a/services/java/Android.mk b/services/java/Android.mk
index 934712c..c756d29 100644
--- a/services/java/Android.mk
+++ b/services/java/Android.mk
@@ -13,7 +13,9 @@
 
 LOCAL_JAVA_LIBRARIES := android.policy
 
+LOCAL_NO_EMMA_INSTRUMENT := true
+LOCAL_NO_EMMA_COMPILE := true
+
 include $(BUILD_JAVA_LIBRARY)
 
 include $(BUILD_DROIDDOC)
-
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/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index f9c1a93..b92480f 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -462,10 +462,11 @@
         mEnabledProviders.add(passiveProvider.getName());
 
         // initialize external network location and geocoder services
+        PackageManager pm = mContext. getPackageManager();
         Resources resources = mContext.getResources();
         String serviceName = resources.getString(
                 com.android.internal.R.string.config_networkLocationProvider);
-        if (serviceName != null) {
+        if (serviceName != null && pm.resolveService(new Intent(serviceName), 0) != null) {
             mNetworkLocationProvider =
                 new LocationProviderProxy(mContext, LocationManager.NETWORK_PROVIDER,
                         serviceName, mLocationHandler);
@@ -473,7 +474,7 @@
         }
 
         serviceName = resources.getString(com.android.internal.R.string.config_geocodeProvider);
-        if (serviceName != null) {
+        if (serviceName != null && pm.resolveService(new Intent(serviceName), 0) != null) {
             mGeocodeProvider = new GeocoderProxy(mContext, serviceName);
         }
 
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 205e308..02ba36d 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -148,7 +148,6 @@
     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 SHOW_TRANSACTIONS = false;
     static final boolean HIDE_STACK_CRAWLS = true;
     static final boolean MEASURE_LATENCY = false;
@@ -4419,8 +4418,7 @@
             final int N = mWindows.size();
             for (int i=0; i<N; i++) {
                 WindowState w = (WindowState)mWindows.get(i);
-                if (w.isVisibleLw() && !w.mObscured
-                        && (w.mOrientationChanging || !w.isDrawnLw())) {
+                if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
                     return;
                 }
             }
@@ -7929,7 +7927,7 @@
             final AppWindowToken atoken = mAppToken;
             return mSurface != null && !mAttachedHidden
                     && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
-                    && (mOrientationChanging || (!mDrawPending && !mCommitDrawPending))
+                    && !mDrawPending && !mCommitDrawPending
                     && !mExiting && !mDestroying;
         }
 
@@ -8033,14 +8031,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;
         }
 
         public boolean fillsScreenLw(int screenWidth, int screenHeight,
@@ -10298,12 +10294,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);
@@ -10699,28 +10689,23 @@
         } else if (animating) {
             requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis());
         }
-        
-        if (DEBUG_FREEZE) Slog.v(TAG, "Layout: mDisplayFrozen=" + mDisplayFrozen
-                + " holdScreen=" + holdScreen);
-        if (!mDisplayFrozen) {
-            mQueue.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);
-            }
+        mQueue.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) {
@@ -10997,8 +10982,6 @@
             mFreezeGcPending = now;
         }
 
-        if (DEBUG_FREEZE) Slog.v(TAG, "*** FREEZING DISPLAY", new RuntimeException());
-        
         mDisplayFrozen = true;
         if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
             mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
@@ -11022,8 +11005,6 @@
             return;
         }
         
-        if (DEBUG_FREEZE) Slog.v(TAG, "*** UNFREEZING DISPLAY", new RuntimeException());
-        
         mDisplayFrozen = false;
         mH.removeMessages(H.APP_FREEZE_TIMEOUT);
         if (PROFILE_ORIENTATION) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 8383ca3..e9e3866 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -6887,8 +6887,6 @@
             enforceCallingPermission(android.Manifest.permission.GET_TASKS,
                     "getRecentTasks()");
 
-            IPackageManager pm = ActivityThread.getPackageManager();
-            
             final int N = mRecentTasks.size();
             ArrayList<ActivityManager.RecentTaskInfo> res
                     = new ArrayList<ActivityManager.RecentTaskInfo>(
@@ -6905,25 +6903,6 @@
                     rti.baseIntent = new Intent(
                             tr.intent != null ? tr.intent : tr.affinityIntent);
                     rti.origActivity = tr.origActivity;
-                    
-                    if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0) {
-                        // Check whether this activity is currently available.
-                        try {
-                            if (rti.origActivity != null) {
-                                if (pm.getActivityInfo(rti.origActivity, 0) == null) {
-                                    continue;
-                                }
-                            } else if (rti.baseIntent != null) {
-                                if (pm.queryIntentActivities(rti.baseIntent,
-                                        null, 0) == null) {
-                                    continue;
-                                }
-                            }
-                        } catch (RemoteException e) {
-                            // Will never happen.
-                        }
-                    }
-                    
                     res.add(rti);
                     maxNum--;
                 }
diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
index afd6ca8..28cefd4 100755
--- a/services/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -276,9 +276,9 @@
         sAGpsInterface->init(&sAGpsCallbacks);
 
     if (!sGpsNiInterface)
-       sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
+        sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
     if (sGpsNiInterface)
-       sGpsNiInterface->init(&sGpsNiCallbacks);
+        sGpsNiInterface->init(&sGpsNiCallbacks);
 
     if (!sGpsDebugInterface)
        sGpsDebugInterface = (const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE);
@@ -533,12 +533,10 @@
 static void android_location_GpsLocationProvider_send_ni_response(JNIEnv* env, jobject obj,
       jint notifId, jint response)
 {
-    if (!sGpsNiInterface) {
+    if (!sGpsNiInterface)
         sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
-    }
-    if (sGpsNiInterface) {
+    if (sGpsNiInterface)
         sGpsNiInterface->respond(notifId, response);
-    }
 }
 
 static jstring android_location_GpsLocationProvider_get_internal_state(JNIEnv* env, jobject obj)
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 32e7176..089a84b 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -747,10 +747,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:
@@ -838,10 +838,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);
 
@@ -972,7 +972,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[]
@@ -1076,10 +1076,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);
@@ -1106,7 +1106,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;
@@ -1502,7 +1502,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
@@ -1510,7 +1510,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;
@@ -1688,7 +1688,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) {
@@ -1708,7 +1708,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
@@ -1830,12 +1830,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,
@@ -1847,18 +1847,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
      */
@@ -1993,7 +1993,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/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 3122722..2a969bb 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..d899430 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;
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index d90c305..9055203 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);
@@ -939,19 +939,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);
@@ -961,7 +961,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
      */
@@ -1066,7 +1066,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
@@ -1139,7 +1139,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
@@ -1170,7 +1170,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/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index ebdd220..cebcf5d 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -16,14 +16,13 @@
 
 package com.android.internal.telephony;
 
-import android.telephony.SmsMessage;
 import android.util.SparseIntArray;
 
 import android.util.Log;
 
 /**
  * This class implements the character set mapping between
- * the GSM SMS 7-bit alphabet specifed in TS 23.038 6.2.1
+ * the GSM SMS 7-bit alphabet specified in TS 23.038 6.2.1
  * and UTF-16
  *
  * {@hide}
@@ -171,7 +170,7 @@
      * array cannot contain more than 255 septets.
      *
      * @param data The text string to encode.
-     * @param header Optional header (includeing length byte) that precedes
+     * @param header Optional header (including length byte) that precedes
      * the encoded data, padded to septet boundary.
      * @return Byte array containing header and encoded data.
      */
@@ -204,7 +203,7 @@
      * the packed septets. The returned array cannot contain more than 255
      * septets.
      *
-     * @param data the data string to endcode
+     * @param data the data string to encode
      * @throws EncodeException if String is too large to encode
      */
     public static byte[] stringToGsm7BitPacked(String data)
@@ -223,7 +222,7 @@
      *
      * @param data the text to convert to septets
      * @param startingSeptetOffset the number of padding septets to put before
-     *  the character data at the begining of the array
+     *  the character data at the beginning of the array
      * @param throwException If true, throws EncodeException on invalid char.
      *   If false, replaces unencodable char with GSM alphabet space char.
      *
@@ -257,7 +256,7 @@
     }
 
     /**
-     * Pack a 7-bit char into its appropirate place in a byte array
+     * Pack a 7-bit char into its appropriate place in a byte array
      *
      * @param bitOffset the bit offset that the septet should be packed at
      *                  (septet index * 7)
diff --git a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 8a5a6ae..5fef6de 100644
--- a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -57,7 +57,7 @@
      * @param destPort 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.
+     *  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>
@@ -67,7 +67,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
@@ -94,7 +94,7 @@
      *  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.
+     *  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>
@@ -140,7 +140,7 @@
      *   <code>RESULT_ERROR_RADIO_OFF</code>
      *   <code>RESULT_ERROR_NULL_PDU</code>.
      *  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
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index d8e313a..b942ad7 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);
     }
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index 764d12e..fb58e14 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -142,7 +142,7 @@
 
     private SmsCounter mCounter;
 
-    private ArrayList mSTrackers = new ArrayList(MO_MSG_QUEUE_LIMIT);
+    private ArrayList<SmsTracker> mSTrackers = new ArrayList<SmsTracker>(MO_MSG_QUEUE_LIMIT);
 
     /** Wake lock to ensure device stays awake while dispatching the SMS intent. */
     private PowerManager.WakeLock mWakeLock;
@@ -264,6 +264,7 @@
         mCm.unregisterForOn(this);
     }
 
+    @Override
     protected void finalize() {
         Log.d(TAG, "SMSDispatcher finalized");
     }
@@ -344,7 +345,7 @@
             msg.obj = null;
             if (mSTrackers.isEmpty() == false) {
                 try {
-                    SmsTracker sTracker = (SmsTracker)mSTrackers.remove(0);
+                    SmsTracker sTracker = mSTrackers.remove(0);
                     sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
                 } catch (CanceledException ex) {
                     Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
@@ -357,7 +358,7 @@
 
         case EVENT_SEND_CONFIRMED_SMS:
             if (mSTrackers.isEmpty() == false) {
-                SmsTracker sTracker = (SmsTracker)mSTrackers.remove(mSTrackers.size() - 1);
+                SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
                 if (isMultipartTracker(sTracker)) {
                     sendMultipartSms(sTracker);
                 } else {
@@ -371,7 +372,7 @@
             if (mSTrackers.isEmpty() == false) {
                 // Remove the latest one.
                 try {
-                    SmsTracker sTracker = (SmsTracker)mSTrackers.remove(mSTrackers.size() - 1);
+                    SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
                     sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
                 } catch (CanceledException ex) {
                     Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
@@ -671,7 +672,7 @@
      * @param destPort 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.
+     *  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>
@@ -681,7 +682,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
@@ -698,7 +699,7 @@
      *  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.
+     *  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>
@@ -734,7 +735,7 @@
      *   <code>RESULT_ERROR_RADIO_OFF</code>
      *   <code>RESULT_ERROR_NULL_PDU</code>.
      *  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
@@ -750,17 +751,17 @@
      * Send a SMS
      *
      * @param smsc the SMSC to send the message through, or NULL for the
-     *  defatult SMSC
+     *  default SMSC
      * @param pdu the raw PDU to send
      * @param sentIntent if not NULL this <code>Intent</code> is
-     *  broadcast when the message is sucessfully sent, or failed.
+     *  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:
      *  <code>RESULT_ERROR_GENERIC_FAILURE</code>
      *  <code>RESULT_ERROR_RADIO_OFF</code>
      *  <code>RESULT_ERROR_NULL_PDU</code>.
      *  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>Intent</code> is
      *  broadcast when the message is delivered to the recipient.  The
@@ -929,14 +930,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;
@@ -945,7 +946,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/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/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index ed93aea..1b08aed 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -51,7 +51,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.lang.Boolean;
 
 
 final class CdmaSMSDispatcher extends SMSDispatcher {
@@ -75,6 +74,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!");
     }
@@ -97,6 +97,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected int dispatchMessage(SmsMessageBase smsb) {
 
         // If sms is null, means there was a parsing error.
@@ -176,7 +177,7 @@
          * TODO(cleanup): Why are we using a getter method for this
          * (and for so many other sms fields)?  Trivial getters and
          * setters like this are direct violations of the style guide.
-         * If the purpose is to protect agaist writes (by not
+         * If the purpose is to protect against writes (by not
          * providing a setter) then any protection is illusory (and
          * hence bad) for cases where the values are not primitives,
          * such as this call for the header.  Since this is an issue
@@ -340,6 +341,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void sendData(String destAddr, String scAddr, int destPort,
             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
@@ -348,6 +350,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void sendText(String destAddr, String scAddr, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
@@ -356,6 +359,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void sendMultipartText(String destAddr, String scAddr,
             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
             ArrayList<PendingIntent> deliveryIntents) {
@@ -364,7 +368,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;
@@ -437,8 +441,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");
@@ -449,11 +454,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
 
@@ -474,16 +481,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/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index c7032ac..a0b5390 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;
 
@@ -33,7 +32,6 @@
 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,7 +43,7 @@
     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.
      */
@@ -802,9 +800,9 @@
      * Create serialized representation for BearerData object.
      * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
      *
-     * @param bearerData an instance of BearerData.
+     * @param bData an instance of BearerData.
      *
-     * @return data byta array of raw encoded SMS bearer data.
+     * @return data byte array of raw encoded SMS bearer data.
      */
     public static byte[] encode(BearerData bData) {
         bData.hasUserDataHeader = ((bData.userData != null) &&
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 6ae316d..fbc3d84 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -57,6 +57,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);
@@ -85,6 +86,7 @@
 
 
     /** {@inheritDoc} */
+    @Override
     protected int dispatchMessage(SmsMessageBase smsb) {
 
         // If sms is null, means there was a parsing error.
@@ -145,6 +147,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void sendData(String destAddr, String scAddr, int destPort,
             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
@@ -153,6 +156,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void sendText(String destAddr, String scAddr, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
@@ -161,6 +165,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void sendMultipartText(String destinationAddress, String scAddress,
             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
             ArrayList<PendingIntent> deliveryIntents) {
@@ -300,8 +305,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");
@@ -316,12 +322,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");
@@ -336,6 +343,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void acknowledgeLastIncomingSms(boolean success, int result, Message response){
         // FIXME unit test leaves cm == null. this should change
         if (mCm != null) {
@@ -344,6 +352,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.");
@@ -351,6 +360,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.");
@@ -358,6 +368,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/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
index 2028ca4..f000d79 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
@@ -86,6 +86,7 @@
     public void dispose() {
     }
 
+    @Override
     protected void finalize() {
         try {
             super.finalize();
@@ -191,6 +192,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 d627baf..4eb667c 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;
@@ -45,7 +44,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;
@@ -303,7 +302,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;
@@ -369,7 +368,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.
@@ -474,7 +473,7 @@
         return bo;
     }
 
-    static class PduParser {
+    private static class PduParser {
         byte pdu[];
         int cur;
         SmsHeader userDataHeader;
@@ -482,10 +481,6 @@
         int mUserDataSeptetPadding;
         int mUserDataSize;
 
-        PduParser(String s) {
-            this(IccUtils.hexStringToBytes(s));
-        }
-
         PduParser(byte[] pdu) {
             this.pdu = pdu;
             cur = 0;
@@ -537,7 +532,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;
@@ -565,7 +560,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)
 
@@ -624,7 +619,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 {
@@ -663,10 +658,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() {
@@ -686,7 +681,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++];
@@ -817,11 +812,13 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public int getProtocolIdentifier() {
         return protocolIdentifier;
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isReplace() {
         return (protocolIdentifier & 0xc0) == 0x40
                 && (protocolIdentifier & 0x3f) > 0
@@ -829,12 +826,14 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isCphsMwiMessage() {
         return ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageClear()
                 || ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageSet();
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isMWIClearMessage() {
         if (isMwi && (mwiSense == false)) {
             return true;
@@ -845,6 +844,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isMWISetMessage() {
         if (isMwi && (mwiSense == true)) {
             return true;
@@ -855,6 +855,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isMwiDontStore() {
         if (isMwi && mwiDontStore) {
             return true;
@@ -874,31 +875,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);
@@ -1150,6 +1154,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public MessageClass getMessageClass() {
         return messageClass;
     }
diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
index 11b3fd6..3675e78 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.gsm.CallFailCause;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
@@ -334,7 +332,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
@@ -440,7 +438,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
      */
@@ -467,7 +465,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) {
@@ -478,7 +476,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"
@@ -495,7 +493,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) {
@@ -506,7 +504,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) {
@@ -517,7 +515,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) {
@@ -529,7 +527,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
@@ -554,7 +552,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) {
@@ -575,7 +573,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) {
@@ -596,7 +594,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) {
@@ -616,7 +614,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) {
@@ -636,7 +634,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) {
@@ -672,7 +670,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) {
@@ -690,7 +688,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) {
@@ -767,7 +765,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];
@@ -826,7 +824,6 @@
         ret[11] = null;
         ret[12] = null;
         ret[13] = null;
-        ret[14] = null;
 
         resultSuccess(result, ret);
     }
@@ -877,7 +874,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) {
@@ -886,7 +883,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) {
@@ -895,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 stopDtmf(Message result) {
@@ -904,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 sendBurstDtmf(String dtmfString, int on, int off, Message result) {
@@ -939,6 +936,7 @@
         unimplemented(response);
     }
 
+    @Deprecated
     public void setupDefaultPDP(String apn, String user, String password, Message result) {
         unimplemented(result);
     }
@@ -950,9 +948,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) {
@@ -1029,7 +1025,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/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/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/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/graphics/java/android/renderscript/Vector2f.java b/test-runner/tests/src/android/test/suitebuilder/examples/constructor/PublicConstructorTest.java
similarity index 65%
copy from graphics/java/android/renderscript/Vector2f.java
copy to test-runner/tests/src/android/test/suitebuilder/examples/constructor/PublicConstructorTest.java
index 567d57fa..a11e25d 100644
--- a/graphics/java/android/renderscript/Vector2f.java
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/constructor/PublicConstructorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 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.
@@ -14,24 +14,15 @@
  * limitations under the License.
  */
 
-package android.renderscript;
+package android.test.suitebuilder.examples.constructor;
 
-import java.lang.Math;
-import android.util.Log;
-
+import junit.framework.TestCase;
 
 /**
- * @hide
- *
- **/
-public class Vector2f {
-    public Vector2f() {
+ * A public constructor test case that should be loaded.
+ */
+public class PublicConstructorTest extends TestCase {
+
+    public void testPublicConstructor() {
     }
-
-    public float x;
-    public float y;
 }
-
-
-
-
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index 77fd3ed..221b218 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -79,19 +79,20 @@
     };
 
     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/events/touch/basic-touch-events.html"); // Requires multi-touch
+        ignoreResultList.add("fast/events/touch/send-oncancel-event.html"); // Expected output is incorrect upstream. See WebKit bug 37830.
         ignoreResultList.add("http/tests/appcache/empty-manifest.html"); // flaky
         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("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
 
-        // Will always fail
-        ignoreResultList.add("dom/svg/level3/xpath"); // XPath not supported
+        // Expected failures due to unsupported features.
+        ignoreResultList.add("fast/events/touch/touch-coords-in-zoom-and-scroll.html"); // Requires eventSender.zoomPageIn(),zoomPageOut()
         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
@@ -104,12 +105,9 @@
         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()
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 81d5b08..ec8a8df 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -742,6 +742,7 @@
         mDumpWebKitData = false;
         mGetDrawtime = false;
         mSaveImagePath = null;
+        setDefaultWebSettings(mWebView);
     }
 
     private long[] getDrawWebViewTime(WebView view, int count) {
@@ -786,6 +787,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());
@@ -798,15 +812,6 @@
         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);
     }
 
     private WebView mWebView;
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