Merge commit 'goog/master' into merge_master
diff --git a/api/current.xml b/api/current.xml
index 9ffc19b23..402c43d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -1145,6 +1145,17 @@
  visibility="public"
 >
 </field>
+<field name="WRITE_EXTERNAL_STORAGE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.WRITE_EXTERNAL_STORAGE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="WRITE_GSERVICES"
  type="java.lang.String"
  transient="false"
@@ -1167,17 +1178,6 @@
  visibility="public"
 >
 </field>
-<field name="WRITE_EXTERNAL_STORAGE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.permission.WRITE_EXTERNAL_STORAGE&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="WRITE_SECURE_SETTINGS"
  type="java.lang.String"
  transient="false"
@@ -28029,7 +28029,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="operations" type="android.content.ContentProviderOperation[]">
+<parameter name="operations" type="java.util.ArrayList&lt;android.content.ContentProviderOperation&gt;">
 </parameter>
 <exception name="OperationApplicationException" type="android.content.OperationApplicationException">
 </exception>
@@ -28373,7 +28373,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="operations" type="android.content.ContentProviderOperation[]">
+<parameter name="operations" type="java.util.ArrayList&lt;android.content.ContentProviderOperation&gt;">
 </parameter>
 <exception name="OperationApplicationException" type="android.content.OperationApplicationException">
 </exception>
@@ -28808,19 +28808,6 @@
  visibility="public"
 >
 </method>
-<method name="withEntity"
- return="android.content.ContentProviderOperation.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="entity" type="android.content.Entity">
-</parameter>
-</method>
 <method name="withExpectedCount"
  return="android.content.ContentProviderOperation.Builder"
  abstract="false"
@@ -28849,7 +28836,7 @@
 <parameter name="selectionArgs" type="java.lang.String[]">
 </parameter>
 </method>
-<method name="withSelectionBackReferences"
+<method name="withSelectionBackReference"
  return="android.content.ContentProviderOperation.Builder"
  abstract="false"
  native="false"
@@ -28859,7 +28846,39 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="backReferences" type="java.util.Map&lt;java.lang.Integer, java.lang.Integer&gt;">
+<parameter name="selectionArgIndex" type="int">
+</parameter>
+<parameter name="previousResult" type="int">
+</parameter>
+</method>
+<method name="withValue"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="value" type="java.lang.Object">
+</parameter>
+</method>
+<method name="withValueBackReference"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="previousResult" type="int">
 </parameter>
 </method>
 <method name="withValueBackReferences"
@@ -29126,7 +29145,7 @@
 >
 <parameter name="authority" type="java.lang.String">
 </parameter>
-<parameter name="operations" type="android.content.ContentProviderOperation[]">
+<parameter name="operations" type="java.util.ArrayList&lt;android.content.ContentProviderOperation&gt;">
 </parameter>
 <exception name="OperationApplicationException" type="android.content.OperationApplicationException">
 </exception>
@@ -29206,23 +29225,6 @@
 <parameter name="values" type="android.content.ContentValues">
 </parameter>
 </method>
-<method name="insertEntity"
- return="android.net.Uri"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="android.net.Uri">
-</parameter>
-<parameter name="entity" type="android.content.Entity">
-</parameter>
-<exception name="RemoteException" type="android.os.RemoteException">
-</exception>
-</method>
 <method name="notifyChange"
  return="void"
  abstract="false"
@@ -29442,23 +29444,6 @@
 <parameter name="selectionArgs" type="java.lang.String[]">
 </parameter>
 </method>
-<method name="updateEntity"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="android.net.Uri">
-</parameter>
-<parameter name="entity" type="android.content.Entity">
-</parameter>
-<exception name="RemoteException" type="android.os.RemoteException">
-</exception>
-</method>
 <method name="validateSyncExtrasBundle"
  return="void"
  abstract="false"
@@ -32623,9 +32608,9 @@
 </interface>
 <class name="Entity"
  extends="java.lang.Object"
- abstract="true"
+ abstract="false"
  static="false"
- final="false"
+ final="true"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -32638,7 +32623,123 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
 </constructor>
+<method name="addSubValue"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getEntityValues"
+ return="android.content.ContentValues"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSubValues"
+ return="java.util.ArrayList&lt;android.content.Entity.NamedContentValues&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Entity.NamedContentValues"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Entity.NamedContentValues"
+ type="android.content.Entity.NamedContentValues"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+</constructor>
+<field name="uri"
+ type="android.net.Uri"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="values"
+ type="android.content.ContentValues"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <interface name="EntityIterator"
  abstract="true"
@@ -38091,6 +38192,16 @@
  visibility="public"
 >
 </field>
+<field name="expandable"
+ type="boolean"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="flags"
  type="int"
  transient="false"
@@ -39948,6 +40059,17 @@
  visibility="public"
 >
 </field>
+<field name="GET_EXPANDABLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="131072"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="GET_GIDS"
  type="int"
  transient="false"
@@ -44377,6 +44499,32 @@
 <parameter name="columnIndex" type="int">
 </parameter>
 </method>
+<method name="isFloat"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
+<method name="isLong"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
 <method name="isNull"
  return="boolean"
  abstract="false"
@@ -44390,6 +44538,19 @@
 <parameter name="columnIndex" type="int">
 </parameter>
 </method>
+<method name="isString"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
 <method name="setWindow"
  return="void"
  abstract="false"
@@ -45470,6 +45631,36 @@
 <parameter name="col" type="int">
 </parameter>
 </method>
+<method name="isFloat"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="row" type="int">
+</parameter>
+<parameter name="col" type="int">
+</parameter>
+</method>
+<method name="isLong"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="row" type="int">
+</parameter>
+<parameter name="col" type="int">
+</parameter>
+</method>
 <method name="isNull"
  return="boolean"
  abstract="false"
@@ -45485,6 +45676,21 @@
 <parameter name="col" type="int">
 </parameter>
 </method>
+<method name="isString"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="row" type="int">
+</parameter>
+<parameter name="col" type="int">
+</parameter>
+</method>
 <method name="newFromParcel"
  return="android.database.CursorWindow"
  abstract="false"
@@ -49914,6 +50120,19 @@
 <parameter name="listener" type="android.gesture.GestureOverlayView.OnGesturePerformedListener">
 </parameter>
 </method>
+<method name="addOnGesturingListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.gesture.GestureOverlayView.OnGesturingListener">
+</parameter>
+</method>
 <method name="cancelClearAnimation"
  return="void"
  abstract="false"
@@ -50114,6 +50333,17 @@
  visibility="public"
 >
 </method>
+<method name="removeAllOnGesturingListeners"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="removeOnGestureListener"
  return="void"
  abstract="false"
@@ -50140,6 +50370,19 @@
 <parameter name="listener" type="android.gesture.GestureOverlayView.OnGesturePerformedListener">
 </parameter>
 </method>
+<method name="removeOnGesturingListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.gesture.GestureOverlayView.OnGesturingListener">
+</parameter>
+</method>
 <method name="setEventsInterceptionEnabled"
  return="void"
  abstract="false"
@@ -50419,6 +50662,40 @@
 </parameter>
 </method>
 </interface>
+<interface name="GestureOverlayView.OnGesturingListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onGesturingEnded"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="overlay" type="android.gesture.GestureOverlayView">
+</parameter>
+</method>
+<method name="onGesturingStarted"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="overlay" type="android.gesture.GestureOverlayView">
+</parameter>
+</method>
+</interface>
 <class name="GesturePoint"
  extends="java.lang.Object"
  abstract="false"
@@ -51855,6 +52132,16 @@
  visibility="public"
 >
 </field>
+<field name="inInputShareable"
+ type="boolean"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="inJustDecodeBounds"
  type="boolean"
  transient="false"
@@ -51875,6 +52162,16 @@
  visibility="public"
 >
 </field>
+<field name="inPurgeable"
+ type="boolean"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="inSampleSize"
  type="int"
  transient="false"
@@ -111435,6 +111732,17 @@
  visibility="public"
 >
 </field>
+<field name="SHOW_WEB_SUGGESTIONS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;show_web_suggestions&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SOUND_EFFECTS_ENABLED"
  type="java.lang.String"
  transient="false"
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 88ad265..2a4a672 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -526,7 +526,7 @@
 
     private void showUsage() {
         System.err.println("usage: am [start|broadcast|instrument|profile]");
-        System.err.println("       am start -D INTENT");
+        System.err.println("       am start [-D] INTENT");
         System.err.println("       am broadcast INTENT");
         System.err.println("       am instrument [-r] [-e <ARG_NAME> <ARG_VALUE>] [-p <PROF_FILE>]");
         System.err.println("                [-w] <COMPONENT> ");
diff --git a/cmds/backup/backup.cpp b/cmds/backup/backup.cpp
index 51ac943..22dd486 100644
--- a/cmds/backup/backup.cpp
+++ b/cmds/backup/backup.cpp
@@ -1,5 +1,20 @@
+/*
+ * 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 <utils/backup_helpers.h>
+#include <utils/BackupHelpers.h>
 #include <utils/String8.h>
 
 #include <fcntl.h>
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 29e57cd..d08fc11 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -162,7 +162,7 @@
         return metrics;
     }
 
-    Resources getTopLevelResources(String appDir, float applicationScale) {
+    Resources getTopLevelResources(String appDir, PackageInfo pkgInfo) {
         synchronized (mPackages) {
             //Log.w(TAG, "getTopLevelResources: " + appDir);
             WeakReference<Resources> wr = mActiveResources.get(appDir);
@@ -181,23 +181,17 @@
             if (assets.addAssetPath(appDir) == 0) {
                 return null;
             }
-            DisplayMetrics metrics = getDisplayMetricsLocked(false);
-            // density used to load resources
-            // scaledDensity is calculated in Resources constructor
-            //
-            boolean usePreloaded = true;
-
-            // TODO: use explicit flag to indicate the compatibility mode.
-            if (applicationScale != 1.0f) {
-                usePreloaded = false;
-                DisplayMetrics newMetrics = new DisplayMetrics();
-                newMetrics.setTo(metrics);
-                float newDensity = metrics.density / applicationScale;
-                newMetrics.updateDensity(newDensity);
-                metrics = newMetrics;
+            ApplicationInfo appInfo;
+            try {
+                appInfo = getPackageManager().getApplicationInfo(
+                        pkgInfo.getPackageName(),
+                        PackageManager.GET_SUPPORTS_DENSITIES | PackageManager.GET_EXPANDABLE);
+            } catch (RemoteException e) {
+                throw new AssertionError(e);
             }
             //Log.i(TAG, "Resource:" + appDir + ", display metrics=" + metrics);
-            r = new Resources(assets, metrics, getConfiguration(), usePreloaded);
+            DisplayMetrics metrics = getDisplayMetricsLocked(false);
+            r = new Resources(assets, metrics, getConfiguration(), appInfo);
             //Log.i(TAG, "Created app resources " + r + ": " + r.getConfiguration());
             // XXX need to remove entries when weak references go away
             mActiveResources.put(appDir, new WeakReference<Resources>(r));
@@ -225,7 +219,6 @@
         private Resources mResources;
         private ClassLoader mClassLoader;
         private Application mApplication;
-        private float mApplicationScale;
 
         private final HashMap<Context, HashMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
             = new HashMap<Context, HashMap<BroadcastReceiver, ReceiverDispatcher>>();
@@ -268,8 +261,6 @@
                 mClassLoader = mSystemContext.getClassLoader();
                 mResources = mSystemContext.getResources();
             }
-
-            mApplicationScale = -1.0f;
         }
 
         public PackageInfo(ActivityThread activityThread, String name,
@@ -288,7 +279,6 @@
             mIncludeCode = true;
             mClassLoader = systemContext.getClassLoader();
             mResources = systemContext.getResources();
-            mApplicationScale = systemContext.getApplicationScale();
         }
 
         public String getPackageName() {
@@ -299,45 +289,6 @@
             return mSecurityViolation;
         }
 
-        public float getApplicationScale() {
-            if (mApplicationScale > 0.0f) {
-                return mApplicationScale;
-            }
-            DisplayMetrics metrics = mActivityThread.getDisplayMetricsLocked(false);
-            // Find out the density scale (relative to 160) of the supported density  that
-            // is closest to the system's density.
-            try {
-                ApplicationInfo ai = getPackageManager().getApplicationInfo(
-                        mPackageName, PackageManager.GET_SUPPORTS_DENSITIES);
-
-                float appScale = -1.0f;
-                if (ai.supportsDensities != null) {
-                    int minDiff = Integer.MAX_VALUE;
-                    for (int density : ai.supportsDensities) {
-                        int tmpDiff = (int) Math.abs(DisplayMetrics.DEVICE_DENSITY - density);
-                        if (tmpDiff == 0) {
-                            appScale = 1.0f;
-                            break;
-                        }
-                        // prefer higher density (appScale>1.0), unless that's only option.
-                        if (tmpDiff < minDiff && appScale < 1.0f) {
-                            appScale = DisplayMetrics.DEVICE_DENSITY / density;
-                            minDiff = tmpDiff;
-                        }
-                    }
-                }
-                if (appScale < 0.0f) {
-                    mApplicationScale = metrics.density;
-                } else {
-                    mApplicationScale = appScale;
-                }
-            } catch (RemoteException e) {
-                throw new AssertionError(e);
-            }
-            if (localLOGV) Log.v(TAG, "appScale=" + mApplicationScale + ", pkg=" + mPackageName);
-            return mApplicationScale;
-        }
-
         /**
          * Gets the array of shared libraries that are listed as
          * used by the given package.
@@ -495,7 +446,7 @@
 
         public Resources getResources(ActivityThread mainThread) {
             if (mResources == null) {
-                mResources = mainThread.getTopLevelResources(mResDir, getApplicationScale());
+                mResources = mainThread.getTopLevelResources(mResDir, this);
             }
             return mResources;
         }
@@ -3606,8 +3557,6 @@
             }
             mConfiguration.updateFrom(config);
             DisplayMetrics dm = getDisplayMetricsLocked(true);
-            DisplayMetrics appDm = new DisplayMetrics();
-            appDm.setTo(dm);
 
             // set it for java, this also affects newly created Resources
             if (config.locale != null) {
@@ -3627,11 +3576,7 @@
                     WeakReference<Resources> v = it.next();
                     Resources r = v.get();
                     if (r != null) {
-                        // keep the original density based on application cale.
-                        appDm.updateDensity(r.getDisplayMetrics().density);
-                        r.updateConfiguration(config, appDm);
-                        // reset
-                        appDm.setTo(dm);
+                        r.updateConfiguration(config, dm);
                         //Log.i(TAG, "Updated app resources " + v.getKey()
                         //        + " " + r + ": " + r.getConfiguration());
                     } else {
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index fe981bc..34aeca1 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -554,19 +554,6 @@
         }
     }
 
-    /**
-     * @hide
-     */
-    @Override
-    public float getApplicationScale() {
-        if (mPackageInfo != null) {
-            return mPackageInfo.getApplicationScale();
-        } else {
-            // same as system density
-            return 1.0f;
-        }
-    }
-
     @Override
     public void setWallpaper(Bitmap bitmap) throws IOException  {
         try {
@@ -2044,8 +2031,7 @@
             ActivityThread.PackageInfo pi = mContext.mMainThread.getPackageInfoNoCheck(app);
             Resources r = mContext.mMainThread.getTopLevelResources(
                     app.uid == Process.myUid() ? app.sourceDir
-                    : app.publicSourceDir,
-                    pi.getApplicationScale());
+                    : app.publicSourceDir, pi);
             if (r != null) {
                 return r;
             }
@@ -2441,6 +2427,16 @@
         }
         
         @Override
+        public void replacePreferredActivity(IntentFilter filter,
+                int match, ComponentName[] set, ComponentName activity) {
+            try {
+                mPM.replacePreferredActivity(filter, match, set, activity);
+            } catch (RemoteException e) {
+                // Should never happen!
+            }
+        }
+
+        @Override
         public void clearPackagePreferredActivities(String packageName) {
             try {
                 mPM.clearPackagePreferredActivities(packageName);
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index e28fd0f..6750d12 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -350,6 +350,14 @@
             scheduleCreateBackupAgent(appInfo, backupMode);
             return true;
         }
+
+        case SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(data);
+            scheduleDestroyBackupAgent(appInfo);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
diff --git a/core/java/android/app/FullBackupAgent.java b/core/java/android/app/FullBackupAgent.java
new file mode 100644
index 0000000..18d62e3
--- /dev/null
+++ b/core/java/android/app/FullBackupAgent.java
@@ -0,0 +1,58 @@
+package android.app;
+
+import android.backup.BackupDataOutput;
+import android.backup.FileBackupHelper;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+/**
+ * Backs up an application's entire /data/data/&lt;package&gt;/... file system.  This
+ * class is used by the desktop full backup mechanism and is not intended for direct
+ * use by applications.
+ * 
+ * {@hide}
+ */
+
+public class FullBackupAgent extends BackupAgent {
+    // !!! TODO: turn off debugging
+    private static final String TAG = "FullBackupAgent";
+    private static final boolean DEBUG = true;
+
+    @Override
+    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+            ParcelFileDescriptor newState) {
+        LinkedList<File> dirsToScan = new LinkedList<File>();
+        ArrayList<String> allFiles = new ArrayList<String>();
+
+        // build the list of files in the app's /data/data tree
+        dirsToScan.add(getFilesDir());
+        if (DEBUG) Log.v(TAG, "Backing up dir tree @ " + getFilesDir().getAbsolutePath() + " :");
+        while (dirsToScan.size() > 0) {
+            File dir = dirsToScan.removeFirst();
+            File[] contents = dir.listFiles();
+            if (contents != null) {
+                for (File f : contents) {
+                    if (f.isDirectory()) {
+                        dirsToScan.add(f);
+                    } else if (f.isFile()) {
+                        if (DEBUG) Log.v(TAG, "    " + f.getAbsolutePath());
+                        allFiles.add(f.getAbsolutePath());
+                    }
+                }
+            }
+        }
+
+        // That's the file set; now back it all up
+        FileBackupHelper.performBackup(this, oldState, data, newState,
+                (String[]) allFiles.toArray());
+    }
+
+    @Override
+    public void onRestore(ParcelFileDescriptor data, ParcelFileDescriptor newState) {
+    }
+
+}
diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl
index 39eb4f1..374423e 100644
--- a/core/java/android/app/ISearchManager.aidl
+++ b/core/java/android/app/ISearchManager.aidl
@@ -23,4 +23,7 @@
 interface ISearchManager {
    SearchableInfo getSearchableInfo(in ComponentName launchActivity, boolean globalSearch);
    List<SearchableInfo> getSearchablesInGlobalSearch();
+   List<SearchableInfo> getSearchablesForWebSearch();
+   SearchableInfo getDefaultSearchableForWebSearch();
+   void setDefaultWebSearch(in ComponentName component);
 }
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 3675ec2..0bb483b 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -1174,7 +1174,7 @@
      */
     protected void launchQuerySearch(int actionKey, String actionMsg)  {
         String query = mSearchAutoComplete.getText().toString();
-        Intent intent = createIntent(Intent.ACTION_SEARCH, null, query, null, 
+        Intent intent = createIntent(Intent.ACTION_SEARCH, null, query, null,
                 actionKey, actionMsg);
         launchIntent(intent);
     }
@@ -1202,13 +1202,17 @@
     protected boolean launchSuggestion(int position, int actionKey, String actionMsg) {
         Cursor c = mSuggestionsAdapter.getCursor();
         if ((c != null) && c.moveToPosition(position)) {
+            mSuggestionsAdapter.callCursorOnClick(c, position);
+
+            // launch the intent
             Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg);
             launchIntent(intent);
+
             return true;
         }
         return false;
     }
-    
+
     /**
      * Launches an intent. Also dismisses the search dialog if not in global search mode.
      */
@@ -1235,9 +1239,6 @@
         if (SearchManager.INTENT_ACTION_CHANGE_SEARCH_SOURCE.equals(action)) {
             handleChangeSourceIntent(intent);
             return true;
-        } else if (SearchManager.INTENT_ACTION_CURSOR_RESPOND.equals(action)) {
-            handleCursorRespondIntent(intent);
-            return true;
         }
         return false;
     }
@@ -1268,34 +1269,14 @@
         String query = intent.getStringExtra(SearchManager.QUERY);
         setUserQuery(query);
     }
-    
-    /**
-     * Handles {@link SearchManager#INTENT_ACTION_CURSOR_RESPOND}.
-     */
-    private void handleCursorRespondIntent(Intent intent) {
-        Cursor c = mSuggestionsAdapter.getCursor();
-        if (c != null) {
-            Bundle response = c.respond(intent.getExtras());
-            
-            // The SHOW_CORPUS_SELECTORS command to the cursor also returns a value in
-            // its bundle, keyed by the same command string, which contains the index
-            // of the "More results..." list item, which we use to instruct the
-            // AutoCompleteTextView's list to scroll to that item when the item is
-            // clicked.
-            if (response.containsKey(SuggestionsAdapter.SHOW_CORPUS_SELECTORS)) {
-                int indexOfMore = response.getInt(SuggestionsAdapter.SHOW_CORPUS_SELECTORS);
-                mSuggestionsAdapter.setListItemToSelect(indexOfMore);
-            }
-        }
-    }
-    
+
     /**
      * Sets the list item selection in the AutoCompleteTextView's ListView.
      */
     public void setListSelection(int index) {
         mSearchAutoComplete.setListSelection(index);
     }
-    
+
     /**
      * Saves the previous component that was searched, so that we can go
      * back to it.
@@ -1365,6 +1346,12 @@
         try {
             // use specific action if supplied, or default action if supplied, or fixed default
             String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
+
+            // some items are display only, or have effect via the cursor respond click reporting.
+            if (SearchManager.INTENT_ACTION_NONE.equals(action)) {
+                return null;
+            }
+
             if (action == null) {
                 action = mSearchable.getSuggestIntentAction();
             }
@@ -1387,7 +1374,7 @@
             Uri dataUri = (data == null) ? null : Uri.parse(data);
 
             String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
-            
+
             String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY);
 
             return createIntent(action, dataUri, query, extraData, actionKey, actionMsg);
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 3bf37c3..f1cc24a 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1148,7 +1148,7 @@
      * @hide
      */
     public final static String SOURCE = "source";
-    
+
     /**
      * Intent extra data key: Use this key with Intent.ACTION_SEARCH and
      * {@link android.content.Intent#getIntExtra content.Intent.getIntExtra()}
@@ -1162,10 +1162,56 @@
     /**
      * Intent extra data key: This key will be used for the extra populated by the
      * {@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA} column.
+     *
      * {@hide}
      */
     public final static String EXTRA_DATA_KEY = "intent_extra_data_key";
-    
+
+    /**
+     * Defines the constants used in the communication between {@link android.app.SearchDialog} and
+     * the global search provider via {@link Cursor#respond(android.os.Bundle)}.
+     *
+     * @hide
+     */
+    public static class DialogCursorProtocol {
+
+        /**
+         * The sent bundle will contain this integer key, with a value set to one of the events
+         * below.
+         */
+        public final static String METHOD = "DialogCursorProtocol.method";
+
+        /**
+         * After data has been refreshed.
+         */
+        public final static int POST_REFRESH = 0;
+        public final static String POST_REFRESH_RECEIVE_ISPENDING
+                = "DialogCursorProtocol.POST_REFRESH.isPending";
+        public final static String POST_REFRESH_RECEIVE_DISPLAY_NOTIFY
+                = "DialogCursorProtocol.POST_REFRESH.displayNotify";
+
+        /**
+         * Just before closing the cursor.
+         */
+        public final static int PRE_CLOSE = 1;
+        public final static String PRE_CLOSE_SEND_MAX_DISPLAY_POS
+                = "DialogCursorProtocol.PRE_CLOSE.sendDisplayPosition";
+
+        /**
+         * When a position has been clicked.
+         */
+        public final static int CLICK = 2;
+        public final static String CLICK_SEND_POSITION
+                = "DialogCursorProtocol.CLICK.sendPosition";
+        public final static String CLICK_RECEIVE_SELECTED_POS
+                = "DialogCursorProtocol.CLICK.receiveSelectedPosition";
+
+        /**
+         * When the threshold received in {@link #POST_REFRESH_RECEIVE_DISPLAY_NOTIFY} is displayed.
+         */
+        public final static int THRESH_HIT = 3;
+    }
+
     /**
      * Intent extra data key: Use this key with Intent.ACTION_SEARCH and
      * {@link android.content.Intent#getStringExtra content.Intent.getStringExtra()}
@@ -1258,28 +1304,6 @@
      */
     public final static String SUGGEST_COLUMN_ICON_2 = "suggest_icon_2";
     /**
-     * Column name for suggestions cursor.  <i>Optional.</i>  If your cursor includes this column,
-     *  then all suggestions will be provided in a format that includes space for two small icons,
-     *  one at the left and one at the right of each suggestion.  The data in the column must
-     *  be a blob that contains a bitmap.
-     * 
-     * This column overrides any icon provided in the {@link #SUGGEST_COLUMN_ICON_1} column.
-     *
-     * @hide
-     */
-    public final static String SUGGEST_COLUMN_ICON_1_BITMAP = "suggest_icon_1_bitmap";
-    /**
-     * Column name for suggestions cursor.  <i>Optional.</i>  If your cursor includes this column,
-     *  then all suggestions will be provided in a format that includes space for two small icons,
-     *  one at the left and one at the right of each suggestion.  The data in the column must
-     *  be a blob that contains a bitmap.
-     * 
-     * This column overrides any icon provided in the {@link #SUGGEST_COLUMN_ICON_2} column.
-     *
-     * @hide
-     */
-    public final static String SUGGEST_COLUMN_ICON_2_BITMAP = "suggest_icon_2_bitmap";
-    /**
      * Column name for suggestions cursor.  <i>Optional.</i>  If this column exists <i>and</i>
      * this element exists at the given row, this is the action that will be used when
      * forming the suggestion's intent.  If the element is not provided, the action will be taken
@@ -1302,9 +1326,10 @@
     /**
      * Column name for suggestions cursor.  <i>Optional.</i>  This column allows suggestions
      *  to provide additional arbitrary data which will be included as an extra under the key
-     *  {@link #EXTRA_DATA_KEY}.
-     * 
-     * @hide pending API council approval
+     *  {@link #EXTRA_DATA_KEY}. For use by the global search system only - if other providers
+     *  attempt to use this column, the value will be overwritten by global search.
+     *
+     * @hide
      */
     public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data";
     /**
@@ -1362,21 +1387,7 @@
      */
     public final static String INTENT_ACTION_CHANGE_SEARCH_SOURCE 
             = "android.search.action.CHANGE_SEARCH_SOURCE";
-    
-    /**
-     * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION},
-     * the search dialog will call {@link Cursor#respond(Bundle)} when the
-     * suggestion is clicked. 
-     * 
-     * The {@link Bundle} argument will be constructed
-     * in the same way as the "extra" bundle included in an Intent constructed 
-     * from the suggestion.
-     * 
-     * @hide Pending API council approval.
-     */
-    public final static String INTENT_ACTION_CURSOR_RESPOND
-            = "android.search.action.CURSOR_RESPOND";
-    
+
     /**
      * Intent action for finding the global search activity.
      * The global search provider should handle this intent.
@@ -1396,6 +1407,34 @@
             = "android.search.action.SEARCH_SETTINGS";
     
     /**
+     * Intent action for starting a web search provider's settings activity.
+     * Web search providers should handle this intent if they have provider-specific
+     * settings to implement.
+     * 
+     * @hide Pending API council approval.
+     */
+    public final static String INTENT_ACTION_WEB_SEARCH_SETTINGS
+            = "android.search.action.WEB_SEARCH_SETTINGS";
+
+    /**
+     * Intent action broadcasted to inform that the searchables list or default have changed.
+     * Components should handle this intent if they cache any searchable data and wish to stay
+     * up to date on changes.
+     *
+     * @hide Pending API council approval.
+     */
+    public final static String INTENT_ACTION_SEARCHABLES_CHANGED
+            = "android.search.action.SEARCHABLES_CHANGED";
+
+    /**
+     * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION},
+     * the search dialog will take no action.
+     *
+     * @hide
+     */
+    public final static String INTENT_ACTION_NONE = "android.search.action.ZILCH";
+    
+    /**
      * Reference to the shared system search service.
      */
     private static ISearchManager sService = getSearchManagerService();
@@ -1505,25 +1544,27 @@
     }
     
     /**
-     * See {@link #setOnDismissListener} for configuring your activity to monitor search UI state.
+     * See {@link SearchManager#setOnDismissListener} for configuring your activity to monitor
+     * search UI state.
      */
     public interface OnDismissListener {
         /**
-         * This method will be called when the search UI is dismissed. To make use if it, you must
-         * implement this method in your activity, and call {@link #setOnDismissListener} to 
-         * register it.
+         * This method will be called when the search UI is dismissed. To make use of it, you must
+         * implement this method in your activity, and call
+         * {@link SearchManager#setOnDismissListener} to register it.
          */
         public void onDismiss();
     }
     
     /**
-     * See {@link #setOnCancelListener} for configuring your activity to monitor search UI state.
+     * See {@link SearchManager#setOnCancelListener} for configuring your activity to monitor
+     * search UI state.
      */
     public interface OnCancelListener {
         /**
          * This method will be called when the search UI is canceled. To make use if it, you must
-         * implement this method in your activity, and call {@link #setOnCancelListener} to 
-         * register it.
+         * implement this method in your activity, and call
+         * {@link SearchManager#setOnCancelListener} to register it.
          */
         public void onCancel();
     }
@@ -1713,4 +1754,48 @@
             return null;
         }
     }
+
+    /**
+     * Returns a list of the searchable activities that handle web searches.
+     *
+     * @return a a list of all searchable activities that handle {@link SearchManager#ACTION_WEB_SEARCH}.
+     *
+     * @hide because SearchableInfo is not part of the API.
+     */
+    public static List<SearchableInfo> getSearchablesForWebSearch() {
+        try {
+            return sService.getSearchablesForWebSearch();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the default searchable activity for web searches.
+     *
+     * @return searchable information for the activity handling web searches by default.
+     *
+     * @hide because SearchableInfo is not part of the API.
+     */
+    public static SearchableInfo getDefaultSearchableForWebSearch() {
+        try {
+            return sService.getDefaultSearchableForWebSearch();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Sets the default searchable activity for web searches.
+     *
+     * @param component Name of the component to set as default activity for web searches.
+     *
+     * @hide
+     */
+    public static void setDefaultWebSearch(ComponentName component) {
+        try {
+            sService.setDefaultWebSearch(component);
+        } catch (RemoteException e) {
+        }
+    }
 }
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
index 4406f8e..e515594 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -20,9 +20,6 @@
 import android.content.Context;
 import android.content.res.Resources.NotFoundException;
 import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
@@ -32,12 +29,15 @@
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.CursorAdapter;
 import android.widget.ImageView;
 import android.widget.ResourceCursorAdapter;
 import android.widget.TextView;
 
+import static android.app.SearchManager.DialogCursorProtocol;
+
 import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.WeakHashMap;
 
 /**
@@ -46,15 +46,7 @@
  * @hide
  */
 class SuggestionsAdapter extends ResourceCursorAdapter {
-    // The value used to query a cursor whether it is still expecting more input,
-    // so we can correctly display (or not display) the 'working' spinner in the search dialog.
-    public static final String IS_WORKING = "isWorking";
-    
-    // The value used to tell a cursor to display the corpus selectors, if this is global
-    // search. Also returns the index of the more results item to allow the SearchDialog
-    // to tell the ListView to scroll to that list item.
-    public static final String SHOW_CORPUS_SELECTORS = "showCorpusSelectors";
-    
+
     private static final boolean DBG = false;
     private static final String LOG_TAG = "SuggestionsAdapter";
     
@@ -70,16 +62,22 @@
     private int mText2Col;
     private int mIconName1Col;
     private int mIconName2Col;
-    private int mIconBitmap1Col;
-    private int mIconBitmap2Col;
-    
+
     // This value is stored in SuggestionsAdapter by the SearchDialog to indicate whether
     // a particular list item should be selected upon the next call to notifyDataSetChanged.
     // This is used to indicate the index of the "More results..." list item so that when
     // the data set changes after a click of "More results...", we can correctly tell the
-    // ListView to scroll to the right line item. It gets reset to -1 every time it is consumed.
-    private int mListItemToSelect = -1;
-    
+    // ListView to scroll to the right line item. It gets reset to NONE every time it
+    // is consumed.
+    private int mListItemToSelect = NONE;
+    static final int NONE = -1;
+
+    // holds the maximum position that has been displayed to the user
+    int mMaxDisplayed = NONE;
+
+    // holds the position that, when displayed, should result in notifying the cursor
+    int mDisplayNotifyPos = NONE;
+
     public SuggestionsAdapter(Context context, SearchDialog searchDialog, SearchableInfo searchable,
             WeakHashMap<String, Drawable> outsideDrawablesCache, boolean globalSearchMode) {
         super(context,
@@ -129,6 +127,11 @@
     @Override
     public void changeCursor(Cursor c) {
         if (DBG) Log.d(LOG_TAG, "changeCursor(" + c + ")");
+
+        if (mCursor != null) {
+            callCursorPreClose(mCursor);
+        }
+
         super.changeCursor(c);
         if (c != null) {
             mFormatCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_FORMAT);
@@ -136,46 +139,75 @@
             mText2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2);
             mIconName1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
             mIconName2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
-            mIconBitmap1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1_BITMAP);
-            mIconBitmap2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2_BITMAP);
         }
-        updateWorking();
     }
-        
+
+    /**
+     * Handle sending and receiving information associated with
+     * {@link DialogCursorProtocol#PRE_CLOSE}.
+     *
+     * @param cursor The cursor to call.
+     */
+    private void callCursorPreClose(Cursor cursor) {
+        if (!mGlobalSearchMode) return;
+        final Bundle request = new Bundle();
+        request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.PRE_CLOSE);
+        request.putInt(DialogCursorProtocol.PRE_CLOSE_SEND_MAX_DISPLAY_POS, mMaxDisplayed);
+        final Bundle response = cursor.respond(request);
+
+        mMaxDisplayed = -1;
+    }
+
     @Override
     public void notifyDataSetChanged() {
+        if (DBG) Log.d(LOG_TAG, "notifyDataSetChanged");
         super.notifyDataSetChanged();
-        updateWorking();
-        if (mListItemToSelect != -1) {
+
+        callCursorPostRefresh(mCursor);
+
+        // look out for the pending item we are supposed to scroll to
+        if (mListItemToSelect != NONE) {
             mSearchDialog.setListSelection(mListItemToSelect);
-            mListItemToSelect = -1;
+            mListItemToSelect = NONE;
         }
     }
-    
+
     /**
-     * Specifies the list item to select upon next call of {@link #notifyDataSetChanged()},
-     * in order to let us scroll the "More results..." list item to the top of the screen
-     * (or as close as it can get) when clicked.
+     * Handle sending and receiving information associated with
+     * {@link DialogCursorProtocol#POST_REFRESH}.
+     * 
+     * @param cursor The cursor to call.
      */
-    public void setListItemToSelect(int index) {
-        mListItemToSelect = index;
+    private void callCursorPostRefresh(Cursor cursor) {
+        if (!mGlobalSearchMode) return;
+        final Bundle request = new Bundle();
+        request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.POST_REFRESH);
+        final Bundle response = cursor.respond(request);
+
+        mSearchDialog.setWorking(
+                response.getBoolean(DialogCursorProtocol.POST_REFRESH_RECEIVE_ISPENDING, false));
+
+        mDisplayNotifyPos =
+                response.getInt(DialogCursorProtocol.POST_REFRESH_RECEIVE_DISPLAY_NOTIFY, -1);
     }
-    
+
     /**
-     * Updates the search dialog according to the current working status of the cursor.
+     * Tell the cursor which position was clicked, handling sending and receiving information
+     * associated with {@link DialogCursorProtocol#CLICK}.
+     *
+     * @param cursor The cursor
+     * @param position The position that was clicked.
      */
-    private void updateWorking() {
-        if (!mGlobalSearchMode || mCursor == null) return;
-        
-        Bundle request = new Bundle();
-        request.putString(SearchManager.EXTRA_DATA_KEY, IS_WORKING);
-        Bundle response = mCursor.respond(request);
-        if (response.containsKey(IS_WORKING)) {
-            boolean isWorking = response.getBoolean(IS_WORKING);
-            mSearchDialog.setWorking(isWorking);
-        }
+    void callCursorOnClick(Cursor cursor, int position) {
+        if (!mGlobalSearchMode) return;
+        final Bundle request = new Bundle(1);
+        request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.CLICK);
+        request.putInt(DialogCursorProtocol.CLICK_SEND_POSITION, position);
+        final Bundle response = cursor.respond(request);
+        mListItemToSelect = response.getInt(
+                DialogCursorProtocol.CLICK_RECEIVE_SELECTED_POS, SuggestionsAdapter.NONE);
     }
-    
+
     /**
      * Tags the view with cached child view look-ups.
      */
@@ -185,7 +217,7 @@
         v.setTag(new ChildViewCache(v));
         return v;
     }
-    
+
     /**
      * Cache of the child views of drop-drown list items, to avoid looking up the children
      * each time the contents of a list item are changed.
@@ -207,15 +239,26 @@
     @Override
     public void bindView(View view, Context context, Cursor cursor) {
         ChildViewCache views = (ChildViewCache) view.getTag();
-        boolean isHtml = false;
-        if (mFormatCol >= 0) {
-            String format = cursor.getString(mFormatCol);
-            isHtml = "html".equals(format);    
+        final int pos = cursor.getPosition();
+
+        // update the maximum position displayed since last refresh
+        if (pos > mMaxDisplayed) {
+            mMaxDisplayed = pos;
         }
+
+        // if the cursor wishes to be notified about this position, send it
+        if (mGlobalSearchMode && mDisplayNotifyPos != NONE && pos == mDisplayNotifyPos) {
+            final Bundle request = new Bundle();
+            request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.THRESH_HIT);
+            mCursor.respond(request);
+            mDisplayNotifyPos = NONE;  // only notify the first time
+        }
+
+        final boolean isHtml = mFormatCol > 0 && "html".equals(cursor.getString(mFormatCol));
         setViewText(cursor, views.mText1, mText1Col, isHtml);
         setViewText(cursor, views.mText2, mText2Col, isHtml);
-        setViewIcon(cursor, views.mIcon1, mIconBitmap1Col, mIconName1Col);
-        setViewIcon(cursor, views.mIcon2, mIconBitmap2Col, mIconName2Col);
+        setViewIcon(cursor, views.mIcon1, mIconName1Col);
+        setViewIcon(cursor, views.mIcon2, mIconName2Col);
     }
     
     private void setViewText(Cursor cursor, TextView v, int textCol, boolean isHtml) {
@@ -237,26 +280,15 @@
         }
     }
     
-    private void setViewIcon(Cursor cursor, ImageView v, int iconBitmapCol, int iconNameCol) {
+    private void setViewIcon(Cursor cursor, ImageView v, int iconNameCol) {
         if (v == null) {
             return;
         }
-        Drawable drawable = null;
-        // First try the bitmap column
-        if (iconBitmapCol >= 0) {
-            byte[] data = cursor.getBlob(iconBitmapCol);
-            if (data != null) {
-                Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
-                if (bitmap != null) {
-                    drawable = new BitmapDrawable(bitmap);
-                }
-            }
+        if (iconNameCol < 0) {
+            return;
         }
-        // If there was no bitmap, try the icon resource column.
-        if (drawable == null && iconNameCol >= 0) {
-            String value = cursor.getString(iconNameCol);
-            drawable = getDrawableFromResourceValue(value);
-        }
+        String value = cursor.getString(iconNameCol);
+        Drawable drawable = getDrawableFromResourceValue(value);
         // Set the icon even if the drawable is null, since we need to clear any
         // previous icon.
         v.setImageDrawable(drawable);
@@ -361,21 +393,36 @@
         
         // First, check the cache.
         Drawable drawable = mOutsideDrawablesCache.get(drawableId);
-        if (drawable != null) return drawable;
+        if (drawable != null) {
+            if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + drawableId);
+            return drawable;
+        }
 
         try {
             // Not cached, try using it as a plain resource ID in the provider's context.
             int resourceId = Integer.parseInt(drawableId);
             drawable = mProviderContext.getResources().getDrawable(resourceId);
+            if (DBG) Log.d(LOG_TAG, "Found icon by resource ID: " + drawableId);
         } catch (NumberFormatException nfe) {
             // The id was not an integer resource id.
             // Let the ContentResolver handle content, android.resource and file URIs.
             try {
                 Uri uri = Uri.parse(drawableId);
-                drawable = Drawable.createFromStream(
-                        mProviderContext.getContentResolver().openInputStream(uri),
-                        null);
+                InputStream stream = mProviderContext.getContentResolver().openInputStream(uri);
+                if (stream != null) {
+                    try {
+                        drawable = Drawable.createFromStream(stream, null);
+                    } finally {
+                        try {
+                            stream.close();
+                        } catch (IOException ex) {
+                            Log.e(LOG_TAG, "Error closing icon stream for " + uri, ex);
+                        }
+                    }
+                }
+                if (DBG) Log.d(LOG_TAG, "Opened icon input stream: " + drawableId);
             } catch (FileNotFoundException fnfe) {
+                if (DBG) Log.d(LOG_TAG, "Icon stream not found: " + drawableId);
                 // drawable = null;
             }
                     
@@ -385,7 +432,7 @@
                 mOutsideDrawablesCache.put(drawableId, drawable);
             }
         } catch (NotFoundException nfe) {
-            // Resource could not be found
+            if (DBG) Log.d(LOG_TAG, "Icon resource not found: " + drawableId);
             // drawable = null;
         }
         
@@ -402,7 +449,7 @@
      */
     public static String getColumnString(Cursor cursor, String columnName) {
         int col = cursor.getColumnIndex(columnName);
-        if (col == -1) {
+        if (col == NONE) {
             return null;
         }
         return cursor.getString(col);
diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java
index 6f0b2ee..30f781e 100644
--- a/core/java/android/backup/BackupManager.java
+++ b/core/java/android/backup/BackupManager.java
@@ -42,6 +42,12 @@
     private IBackupManager mService;
 
     /**
+     * Defined backup transports understood by {@link IBackupManager.selectBackupTransport}.
+     */
+    public static final int TRANSPORT_ADB = 1;
+    public static final int TRANSPORT_GOOGLE = 2;
+
+    /**
      * Constructs a BackupManager object through which the application can
      * communicate with the Android backup system.
      *
diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl
index 3468d70..f5b82fe 100644
--- a/core/java/android/backup/IBackupManager.aidl
+++ b/core/java/android/backup/IBackupManager.aidl
@@ -36,20 +36,28 @@
     /**
      * Notifies the Backup Manager Service that an agent has become available.  This
      * method is only invoked by the Activity Manager.
-     * !!! TODO: permission
      */
     oneway void agentConnected(String packageName, IBinder agent);
 
     /**
      * Notify the Backup Manager Service that an agent has unexpectedly gone away.
      * This method is only invoked by the Activity Manager.
-     * !!! TODO: permission
      */
     oneway void agentDisconnected(String packageName);
 
     /**
-     * Schedule a full backup of the given package.
-     * !!! TODO: permission
+     * Schedule a full backup of the given package.  Callers must hold the
+     * android.permission.BACKUP permission to use this method.
      */
     oneway void scheduleFullBackup(String packageName);
+
+    /**
+     * Specify a default backup transport.  Callers must hold the
+     * android.permission.BACKUP permission to use this method.
+     *
+     * @param transportID The ID of the transport to select.  This should be one
+     * of {@link BackupManager.TRANSPORT_GOOGLE} or {@link BackupManager.TRANSPORT_ADB}.
+     * @return The ID of the previously selected transport.
+     */
+    int selectBackupTransport(int transportID);
 }
diff --git a/core/java/android/bluetooth/BluetoothInputStream.java b/core/java/android/bluetooth/BluetoothInputStream.java
index ceae70c..e6f501c 100644
--- a/core/java/android/bluetooth/BluetoothInputStream.java
+++ b/core/java/android/bluetooth/BluetoothInputStream.java
@@ -24,7 +24,6 @@
  *
  * Used to write to a Bluetooth socket.
  *
- * TODO: Implement bulk writes (instead of one byte at a time).
  * @hide
  */
 /*package*/ final class BluetoothInputStream extends InputStream {
@@ -54,9 +53,46 @@
      * @return the byte read or -1 if the end of stream has been reached.
      * @throws IOException
      *             if the stream is closed or another IOException occurs.
-     * @since Android 1.0
+     * @since Android 1.5
      */
     public int read() throws IOException {
-        return mSocket.readNative();
+        byte b[] = new byte[1];
+        int ret = mSocket.readNative(b, 0, 1);
+        if (ret == 1) {
+            return (int)b[0];
+        } else {
+            return -1;
+        }
+    }
+
+    /**
+     * Reads at most {@code length} bytes from this stream and stores them in
+     * the byte array {@code b} starting at {@code offset}.
+     *
+     * @param b
+     *            the byte array in which to store the bytes read.
+     * @param offset
+     *            the initial position in {@code buffer} to store the bytes
+     *            read from this stream.
+     * @param length
+     *            the maximum number of bytes to store in {@code b}.
+     * @return the number of bytes actually read or -1 if the end of the stream
+     *         has been reached.
+     * @throws IndexOutOfBoundsException
+     *             if {@code offset < 0} or {@code length < 0}, or if
+     *             {@code offset + length} is greater than the length of
+     *             {@code b}.
+     * @throws IOException
+     *             if the stream is closed or another IOException occurs.
+     * @since Android 1.5
+     */
+    public int read(byte[] b, int offset, int length) throws IOException {
+        if (b == null) {
+            throw new NullPointerException("byte array is null");
+        }
+        if ((offset | length) < 0 || length > b.length - offset) {
+            throw new ArrayIndexOutOfBoundsException("invalid offset or length");
+        }
+        return mSocket.readNative(b, offset, length);
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothOutputStream.java b/core/java/android/bluetooth/BluetoothOutputStream.java
index 32e6d17..7e2ead4 100644
--- a/core/java/android/bluetooth/BluetoothOutputStream.java
+++ b/core/java/android/bluetooth/BluetoothOutputStream.java
@@ -24,7 +24,6 @@
  *
  * Used to read from a Bluetooth socket.
  *
- * TODO: Implement bulk reads (instead of one byte at a time).
  * @hide
  */
 /*package*/ final class BluetoothOutputStream extends OutputStream {
@@ -52,6 +51,37 @@
      * @since Android 1.0
      */
     public void write(int oneByte) throws IOException {
-        mSocket.writeNative(oneByte);
+        byte b[] = new byte[1];
+        b[0] = (byte)oneByte;
+        mSocket.writeNative(b, 0, 1);
+    }
+
+    /**
+     * Writes {@code count} bytes from the byte array {@code buffer} starting
+     * at position {@code offset} to this stream.
+     *
+     * @param b
+     *            the buffer to be written.
+     * @param offset
+     *            the start position in {@code buffer} from where to get bytes.
+     * @param count
+     *            the number of bytes from {@code buffer} to write to this
+     *            stream.
+     * @throws IOException
+     *             if an error occurs while writing to this stream.
+     * @throws IndexOutOfBoundsException
+     *             if {@code offset < 0} or {@code count < 0}, or if
+     *             {@code offset + count} is bigger than the length of
+     *             {@code buffer}.
+     * @since Android 1.0
+     */
+    public void write(byte[] b, int offset, int count) throws IOException {
+        if (b == null) {
+            throw new NullPointerException("buffer is null");
+        }
+        if ((offset | count) < 0 || count > b.length - offset) {
+            throw new IndexOutOfBoundsException("invalid offset or length");
+        }
+        mSocket.writeNative(b, offset, count);
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index ca46701..f3baeab 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -27,7 +27,7 @@
  * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is
  * also known as the Serial Port Profile (SPP).
  *
- * TODO: Consider implementing SCO and L2CAP sockets.
+ * TODO: Consider exposing L2CAP sockets.
  * TODO: Clean up javadoc grammer and formatting.
  * TODO: Remove @hide
  * @hide
@@ -45,9 +45,10 @@
      *                     insufficient permissions.
      */
     public static BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException {
-        BluetoothServerSocket socket = new BluetoothServerSocket(true, true);
+        BluetoothServerSocket socket = new BluetoothServerSocket(
+                BluetoothSocket.TYPE_RFCOMM, true, true, port);
         try {
-            socket.mSocket.bindListenNative(port);
+            socket.mSocket.bindListenNative();
         } catch (IOException e) {
             try {
                 socket.close();
@@ -65,9 +66,31 @@
      *                     insufficient permissions.
      */
     public static BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
-        BluetoothServerSocket socket = new BluetoothServerSocket(false, false);
+        BluetoothServerSocket socket = new BluetoothServerSocket(
+                BluetoothSocket.TYPE_RFCOMM, false, false, port);
         try {
-            socket.mSocket.bindListenNative(port);
+            socket.mSocket.bindListenNative();
+        } catch (IOException e) {
+            try {
+                socket.close();
+            } catch (IOException e2) { }
+            throw e;
+        }
+        return socket;
+    }
+
+    /**
+     * Construct a SCO server socket.
+     * Call #accept to retrieve connections to this socket.
+     * @return A SCO BluetoothServerSocket
+     * @throws IOException On error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     */
+    public static BluetoothServerSocket listenUsingScoOn() throws IOException {
+        BluetoothServerSocket socket = new BluetoothServerSocket(
+                BluetoothSocket.TYPE_SCO, false, false, -1);
+        try {
+            socket.mSocket.bindListenNative();
         } catch (IOException e) {
             try {
                 socket.close();
@@ -79,13 +102,16 @@
 
     /**
      * Construct a socket for incoming connections.
-     * @param auth    Require the remote device to be authenticated
-     * @param encrypt Require the connection to be encrypted
+     * @param type    type of socket
+     * @param auth    require the remote device to be authenticated
+     * @param encrypt require the connection to be encrypted
+     * @param port    remote port
      * @throws IOException On error, for example Bluetooth not available, or
      *                     insufficient priveleges
      */
-    private BluetoothServerSocket(boolean auth, boolean encrypt) throws IOException {
-        mSocket = new BluetoothSocket(-1, auth, encrypt, null, -1);
+    private BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
+            throws IOException {
+        mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port);
     }
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index fd8885e..de1f326 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -29,13 +29,19 @@
  * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is
  * also known as the Serial Port Profile (SPP).
  *
- * TODO: Consider implementing SCO and L2CAP sockets.
+ * TODO: Consider exposing L2CAP sockets.
  * TODO: Clean up javadoc grammer and formatting.
  * TODO: Remove @hide
  * @hide
  */
 public final class BluetoothSocket implements Closeable {
-    private final int mPort;
+    /** Keep TYPE_RFCOMM etc in sync with BluetoothSocket.cpp */
+    /*package*/ static final int TYPE_RFCOMM = 1;
+    /*package*/ static final int TYPE_SCO = 2;
+    /*package*/ static final int TYPE_L2CAP = 3;
+
+    private final int mType;  /* one of TYPE_RFCOMM etc */
+    private final int mPort;  /* RFCOMM channel or L2CAP psm */
     private final String mAddress;    /* remote address */
     private final boolean mAuth;
     private final boolean mEncrypt;
@@ -57,7 +63,7 @@
      */
     public static BluetoothSocket createRfcommSocket(String address, int port)
             throws IOException {
-        return new BluetoothSocket(-1, true, true, address, port);
+        return new BluetoothSocket(TYPE_RFCOMM, -1, true, true, address, port);
     }
 
     /**
@@ -74,11 +80,25 @@
      */
     public static BluetoothSocket createInsecureRfcommSocket(String address, int port)
             throws IOException {
-        return new BluetoothSocket(-1, false, false, address, port);
+        return new BluetoothSocket(TYPE_RFCOMM, -1, false, false, address, port);
+    }
+
+    /**
+     * Construct a SCO socket ready to start an outgoing connection.
+     * Call #connect on the returned #BluetoothSocket to begin the connection.
+     * @param address remote Bluetooth address that this socket can connect to
+     * @return a SCO BluetoothSocket
+     * @throws IOException on error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     */
+    public static BluetoothSocket createScoSocket(String address, int port)
+            throws IOException {
+        return new BluetoothSocket(TYPE_SCO, -1, true, true, address, port);
     }
 
     /**
      * Construct a Bluetooth.
+     * @param type    type of socket
      * @param fd      fd to use for connected socket, or -1 for a new socket
      * @param auth    require the remote device to be authenticated
      * @param encrypt require the connection to be encrypted
@@ -87,8 +107,9 @@
      * @throws IOException On error, for example Bluetooth not available, or
      *                     insufficient priveleges
      */
-    /*package*/ BluetoothSocket(int fd, boolean auth, boolean encrypt, String address, int port)
-            throws IOException {
+    /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
+            int port) throws IOException {
+        mType = type;
         mAuth = auth;
         mEncrypt = encrypt;
         mAddress = address;
@@ -120,7 +141,7 @@
      * @throws IOException On error, for example connection failure
      */
     public void connect() throws IOException {
-        connectNative(mAddress, mPort, -1);
+        connectNative();
     }
 
     /**
@@ -163,14 +184,14 @@
         return mOutputStream;
     }
 
-    private native void initSocketNative();
-    private native void initSocketFromFdNative(int fd);
-    private native void connectNative(String address, int port, int timeout);
-    /*package*/ native void bindListenNative(int port) throws IOException;
+    private native void initSocketNative() throws IOException;
+    private native void initSocketFromFdNative(int fd) throws IOException;
+    private native void connectNative() throws IOException;
+    /*package*/ native void bindListenNative() throws IOException;
     /*package*/ native BluetoothSocket acceptNative(int timeout) throws IOException;
-    /*package*/ native int availableNative();
-    /*package*/ native int readNative();
-    /*package*/ native void writeNative(int data);
-    /*package*/ native void closeNative();
-    private native void destroyNative();
+    /*package*/ native int availableNative() throws IOException;
+    /*package*/ native int readNative(byte[] b, int offset, int length) throws IOException;
+    /*package*/ native int writeNative(byte[] b, int offset, int length) throws IOException;
+    /*package*/ native void closeNative() throws IOException;
+    private native void destroyNative() throws IOException;
 }
diff --git a/core/java/android/content/AbstractSyncableContentProvider.java b/core/java/android/content/AbstractSyncableContentProvider.java
index 05781f4..e628dcd 100644
--- a/core/java/android/content/AbstractSyncableContentProvider.java
+++ b/core/java/android/content/AbstractSyncableContentProvider.java
@@ -406,7 +406,7 @@
      * }
      * </pre>
      *
-     * @hide This method should be used only when {@link #applyBatch} is not enough and must be
+     * @hide This method should be used only when {@link ContentProvider#applyBatch} is not enough and must be
      * used with {@link #endBatch}.
      * e.g. If returned value has to be used during one transaction, this method might be useful.
      */
@@ -461,7 +461,7 @@
         }
     }
 
-    public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations)
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws OperationApplicationException {
         boolean successful = false;
         beginBatch();
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index bb25b68..4e631c4 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -32,6 +32,7 @@
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.util.ArrayList;
 
 /**
  * Content providers are one of the primary building blocks of Android applications, providing
@@ -156,7 +157,7 @@
             return ContentProvider.this.insertEntity(uri, entities);
         }
 
-        public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations)
+        public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
                 throws OperationApplicationException {
             for (ContentProviderOperation operation : operations) {
                 if (operation.isReadOperation()) {
@@ -641,11 +642,12 @@
      * @throws OperationApplicationException thrown if an application fails.
      * See {@link ContentProviderOperation#apply} for more information.
      */
-    public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations)
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws OperationApplicationException {
-        ContentProviderResult[] results = new ContentProviderResult[operations.length];
-        for (int i = 0; i < operations.length; i++) {
-            results[i] = operations[i].apply(this, results, i);
+        final int numOperations = operations.size();
+        final ContentProviderResult[] results = new ContentProviderResult[numOperations];
+        for (int i = 0; i < numOperations; i++) {
+            results[i] = operations.get(i).apply(this, results, i);
         }
         return results;
     }
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 08d4fca..452653e 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -23,6 +23,7 @@
 import android.content.res.AssetFileDescriptor;
 
 import java.io.FileNotFoundException;
+import java.util.ArrayList;
 
 /**
  * The public interface object used to interact with a {@link ContentProvider}. This is obtained by
@@ -105,7 +106,7 @@
     }
 
     /** see {@link ContentProvider#applyBatch} */
-    public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations)
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws RemoteException, OperationApplicationException {
         return mContentProvider.applyBatch(operations);
     }
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 4747726..a4c217b 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -33,6 +33,7 @@
 import android.os.Parcelable;
 
 import java.io.FileNotFoundException;
+import java.util.ArrayList;
 
 /**
  * {@hide}
@@ -179,8 +180,12 @@
                 case APPLY_BATCH_TRANSACTION:
                 {
                     data.enforceInterface(IContentProvider.descriptor);
-                    final ContentProviderOperation[] operations =
-                            data.createTypedArray(ContentProviderOperation.CREATOR);
+                    final int numOperations = data.readInt();
+                    final ArrayList<ContentProviderOperation> operations =
+                            new ArrayList<ContentProviderOperation>(numOperations);
+                    for (int i = 0; i < numOperations; i++) {
+                        operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data));
+                    }
                     final ContentProviderResult[] results = applyBatch(operations);
                     reply.writeNoException();
                     reply.writeTypedArray(results, 0);
@@ -471,13 +476,16 @@
         return count;
     }
 
-    public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations)
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws RemoteException, OperationApplicationException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
 
         data.writeInterfaceToken(IContentProvider.descriptor);
-        data.writeTypedArray(operations, 0);
+        data.writeInt(operations.size());
+        for (ContentProviderOperation operation : operations) {
+            operation.writeToParcel(data, 0);
+        }
         mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0);
 
         DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply);
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index ce92198..001af16e 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -20,12 +20,11 @@
 import android.database.Cursor;
 import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Debug;
 
 import java.util.Map;
 import java.util.HashMap;
 
-import dalvik.system.VMStack;
-
 public class ContentProviderOperation implements Parcelable {
     private final static int TYPE_INSERT = 1;
     private final static int TYPE_UPDATE = 2;
@@ -37,7 +36,6 @@
     private final String mSelection;
     private final String[] mSelectionArgs;
     private final ContentValues mValues;
-    private final Entity mEntity;
     private final Integer mExpectedCount;
     private final ContentValues mValuesBackReferences;
     private final Map<Integer, Integer> mSelectionArgsBackReferences;
@@ -52,7 +50,6 @@
         mType = builder.mType;
         mUri = builder.mUri;
         mValues = builder.mValues;
-        mEntity = builder.mEntity;
         mSelection = builder.mSelection;
         mSelectionArgs = builder.mSelectionArgs;
         mExpectedCount = builder.mExpectedCount;
@@ -60,11 +57,10 @@
         mValuesBackReferences = builder.mValuesBackReferences;
     }
 
-    private ContentProviderOperation(Parcel source, ClassLoader classLoader) {
+    private ContentProviderOperation(Parcel source) {
         mType = source.readInt();
         mUri = Uri.CREATOR.createFromParcel(source);
         mValues = source.readInt() != 0 ? ContentValues.CREATOR.createFromParcel(source) : null;
-        mEntity = (Entity) source.readParcelable(classLoader);
         mSelection = source.readInt() != 0 ? source.readString() : null;
         mSelectionArgs = source.readInt() != 0 ? source.readStringArray() : null;
         mExpectedCount = source.readInt() != 0 ? source.readInt() : null;
@@ -92,7 +88,6 @@
         } else {
             dest.writeInt(0);
         }
-        dest.writeParcelable(mEntity, 0);
         if (mSelection != null) {
             dest.writeInt(1);
             dest.writeString(mSelection);
@@ -184,7 +179,7 @@
      * Applies this operation using the given provider. The backRefs array is used to resolve any
      * back references that were requested using
      * {@link Builder#withValueBackReferences(ContentValues)} and
-     * {@link Builder#withSelectionBackReferences(java.util.Map)}.
+     * {@link Builder#withSelectionBackReference}.
      * @param provider the {@link ContentProvider} on which this batch is applied
      * @param backRefs a {@link ContentProviderResult} array that will be consulted
      * to resolve any requested back references.
@@ -201,12 +196,7 @@
                 resolveSelectionArgsBackReferences(backRefs, numBackRefs);
 
         if (mType == TYPE_INSERT) {
-            Uri newUri;
-            if (mEntity != null) {
-                newUri = provider.insertEntity(mUri, mEntity);
-            } else {
-                newUri = provider.insert(mUri, values);
-            }
+            Uri newUri = provider.insert(mUri, values);
             if (newUri == null) {
                 throw new OperationApplicationException("insert failed");
             }
@@ -217,11 +207,7 @@
         if (mType == TYPE_DELETE) {
             numRows = provider.delete(mUri, mSelection, selectionArgs);
         } else if (mType == TYPE_UPDATE) {
-            if (mEntity != null) {
-                numRows = provider.updateEntity(mUri, mEntity);
-            } else {
-                numRows = provider.update(mUri, values, mSelection, selectionArgs);
-            }
+            numRows = provider.update(mUri, values, mSelection, selectionArgs);
         } else if (mType == TYPE_COUNT) {
             Cursor cursor = provider.query(mUri, COUNT_COLUMNS, mSelection, selectionArgs, null);
             try {
@@ -322,7 +308,7 @@
      */
     private static String backRefToValue(ContentProviderResult[] backRefs, int numBackRefs,
             Integer backRefIndex) {
-        if (backRefIndex > numBackRefs) {
+        if (backRefIndex >= numBackRefs) {
             throw new ArrayIndexOutOfBoundsException("asked for back ref " + backRefIndex
                     + " but there are only " + numBackRefs + " back refs");
         }
@@ -343,7 +329,7 @@
     public static final Creator<ContentProviderOperation> CREATOR =
             new Creator<ContentProviderOperation>() {
         public ContentProviderOperation createFromParcel(Parcel source) {
-            return new ContentProviderOperation(source, VMStack.getCallingClassLoader2());
+            return new ContentProviderOperation(source);
         }
 
         public ContentProviderOperation[] newArray(int size) {
@@ -368,7 +354,6 @@
         private String mSelection;
         private String[] mSelectionArgs;
         private ContentValues mValues;
-        private Entity mEntity;
         private Integer mExpectedCount;
         private ContentValues mValuesBackReferences;
         private Map<Integer, Integer> mSelectionArgsBackReferences;
@@ -384,14 +369,6 @@
 
         /** Create a ContentProviderOperation from this {@link Builder}. */
         public ContentProviderOperation build() {
-            if (mValues != null && mEntity != null) {
-                throw new IllegalArgumentException("you are not allowed to specify both an entity "
-                        + "and a values");
-            }
-            if (mEntity != null && mValuesBackReferences != null) {
-                throw new IllegalArgumentException("you are not allowed to specify both an entity "
-                        + "and a values backreference");
-            }
             return new ContentProviderOperation(this);
         }
 
@@ -414,25 +391,46 @@
         }
 
         /**
-         * Add a {@link Map} of back references. The integer key is the index of the selection arg
-         * to set and the integer value is the index of the previous result whose
-         * value should be used for the arg. If any value at that index of the selection arg
+         * Add a ContentValues back reference.
+         * A column value from the back references takes precedence over a value specified in
+         * {@link #withValues}.
+         * This can only be used with builders of type insert or update.
+         * @return this builder, to allow for chaining.
+         */
+        public Builder withValueBackReference(String key, int previousResult) {
+            if (mType != TYPE_INSERT && mType != TYPE_UPDATE) {
+                throw new IllegalArgumentException(
+                        "only inserts and updates can have value back-references");
+            }
+            if (mValuesBackReferences == null) {
+                mValuesBackReferences = new ContentValues();
+            }
+            mValuesBackReferences.put(key, previousResult);
+            return this;
+        }
+
+        /**
+         * Add a back references as a selection arg. Any value at that index of the selection arg
          * that was specified by {@link #withSelection} will be overwritten.
          * This can only be used with builders of type update, delete, or count query.
          * @return this builder, to allow for chaining.
          */
-        public Builder withSelectionBackReferences(Map<Integer, Integer> backReferences) {
+        public Builder withSelectionBackReference(int selectionArgIndex, int previousResult) {
             if (mType != TYPE_COUNT && mType != TYPE_UPDATE && mType != TYPE_DELETE) {
                 throw new IllegalArgumentException(
                         "only deletes, updates and counts can have selection back-references");
             }
-            mSelectionArgsBackReferences = backReferences;
+            if (mSelectionArgsBackReferences == null) {
+                mSelectionArgsBackReferences = new HashMap<Integer, Integer>();
+            }
+            mSelectionArgsBackReferences.put(selectionArgIndex, previousResult);
             return this;
         }
 
         /**
          * The ContentValues to use. This may be null. These values may be overwritten by
-         * the corresponding value specified by {@link #withValueBackReferences(ContentValues)}.
+         * the corresponding value specified by {@link #withValueBackReference} or by
+         * future calls to {@link #withValues} or {@link #withValue}.
          * This can only be used with builders of type insert or update.
          * @return this builder, to allow for chaining.
          */
@@ -440,29 +438,60 @@
             if (mType != TYPE_INSERT && mType != TYPE_UPDATE) {
                 throw new IllegalArgumentException("only inserts and updates can have values");
             }
-            mValues = values;
+            if (mValues == null) {
+                mValues = new ContentValues();
+            }
+            mValues.putAll(values);
             return this;
         }
 
         /**
-         * The ContentValues to use. This may be null. These values may be overwritten by
-         * the corresponding value specified by {@link #withValueBackReferences(ContentValues)}.
+         * A value to insert or update. This value may be overwritten by
+         * the corresponding value specified by {@link #withValueBackReference}.
          * This can only be used with builders of type insert or update.
+         * @param key the name of this value
+         * @param value the value itself. the type must be acceptable for insertion by
+         * {@link ContentValues#put}
          * @return this builder, to allow for chaining.
          */
-        public Builder withEntity(Entity entity) {
+        public Builder withValue(String key, Object value) {
             if (mType != TYPE_INSERT && mType != TYPE_UPDATE) {
-                throw new IllegalArgumentException("only inserts and updates can have an entity");
+                throw new IllegalArgumentException("only inserts and updates can have values");
             }
-            mEntity = entity;
+            if (mValues == null) {
+                mValues = new ContentValues();
+            }
+            if (value == null) {
+                mValues.putNull(key);
+            } else if (value instanceof String) {
+                mValues.put(key, (String) value);
+            } else if (value instanceof Byte) {
+                mValues.put(key, (Byte) value);
+            } else if (value instanceof Short) {
+                mValues.put(key, (Short) value);
+            } else if (value instanceof Integer) {
+                mValues.put(key, (Integer) value);
+            } else if (value instanceof Long) {
+                mValues.put(key, (Long) value);
+            } else if (value instanceof Float) {
+                mValues.put(key, (Float) value);
+            } else if (value instanceof Double) {
+                mValues.put(key, (Double) value);
+            } else if (value instanceof Boolean) {
+                mValues.put(key, (Boolean) value);
+            } else if (value instanceof byte[]) {
+                mValues.put(key, (byte[]) value);
+            } else {
+                throw new IllegalArgumentException("bad value type: " + value.getClass().getName());
+            }
             return this;
         }
-
+        
         /**
          * The selection and arguments to use. An occurrence of '?' in the selection will be
          * replaced with the corresponding occurence of the selection argument. Any of the
          * selection arguments may be overwritten by a selection argument back reference as
-         * specified by {@link #withSelectionBackReferences}.
+         * specified by {@link #withSelectionBackReference}.
          * This can only be used with builders of type update, delete, or count query.
          * @return this builder, to allow for chaining.
          */
diff --git a/core/java/android/content/ContentProviderResult.java b/core/java/android/content/ContentProviderResult.java
index 34aaa6d..5d188ef 100644
--- a/core/java/android/content/ContentProviderResult.java
+++ b/core/java/android/content/ContentProviderResult.java
@@ -74,4 +74,11 @@
             return new ContentProviderResult[size];
         }
     };
+
+    public String toString() {
+        if (uri != null) {
+            return "ContentProviderResult(uri=" + uri.toString() + ")";
+        }
+        return "ContentProviderResult(count=" + count + ")";
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index f7b52fa..a01c5d1 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -41,6 +41,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.List;
+import java.util.ArrayList;
 
 
 /**
@@ -567,50 +568,6 @@
     }
 
     /**
-     * Inserts an Entity at the given URL.
-     *
-     * @param uri The URL of the table to insert into.
-     * @param entity an Entity to insert at uri. This must be the same Entity subtype that the
-     *   matching content provider expects at that uri.
-     * @return the URL of the newly created row.
-     * @throws RemoteException thrown if a RemoteException is encountered while attempting
-     *   to communicate with a remote provider.
-     */
-    public final Uri insertEntity(Uri uri, Entity entity) throws RemoteException {
-        ContentProviderClient provider = acquireContentProviderClient(uri);
-        if (provider == null) {
-            throw new IllegalArgumentException("Unknown URL " + uri);
-        }
-        try {
-            return provider.insertEntity(uri, entity);
-        } finally {
-            provider.release();
-        }
-    }
-
-    /**
-     * Replaces the Entity at the given URL with the provided entity.
-     *
-     * @param uri The URL of the entity to update.
-     * @param entity the new version of the entity
-     * @return the number of rows that are updated. Will be 0 if an entity no longer exists
-     * at uri otherwise it will be 1.
-     * @throws RemoteException thrown if a RemoteException is encountered while attempting
-     *   to communicate with a remote provider.
-     */
-    public final int updateEntity(Uri uri, Entity entity) throws RemoteException {
-        ContentProviderClient provider = acquireContentProviderClient(uri);
-        if (provider == null) {
-            throw new IllegalArgumentException("Unknown URL " + uri);
-        }
-        try {
-            return provider.updateEntity(uri, entity);
-        } finally {
-            provider.release();
-        }
-    }
-
-    /**
      * Applies each of the {@link ContentProviderOperation} objects and returns an array
      * of their results. Passes through OperationApplicationException, which may be thrown
      * by the call to {@link ContentProviderOperation#apply}.
@@ -627,7 +584,7 @@
      *   to communicate with a remote provider.
      */
     public ContentProviderResult[] applyBatch(String authority,
-            ContentProviderOperation[] operations)
+            ArrayList<ContentProviderOperation> operations)
             throws RemoteException, OperationApplicationException {
         ContentProviderClient provider = acquireContentProviderClient(authority);
         if (provider == null) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1267e83..64d8c63 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -527,16 +527,6 @@
     public abstract int getWallpaperDesiredMinimumHeight();
 
     /**
-     * Returns the scale in which the application will be drawn on the
-     * screen. This is usually 1.0f if the application supports the device's
-     * resolution/density. This will be 1.5f, for example, if the application
-     * that supports only 160 density runs on 240 density screen.
-     *
-     * @hide
-     */
-    public abstract float getApplicationScale();
-
-    /**
      * Change the current system wallpaper to a bitmap.  The given bitmap is
      * converted to a PNG and stored as the wallpaper.  On success, the intent
      * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 25b2cae..36e1c34 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -419,12 +419,4 @@
         throws PackageManager.NameNotFoundException {
         return mBase.createPackageContext(packageName, flags);
     }
-
-    /**
-     * @hide
-     */
-    @Override
-    public float getApplicationScale() {
-        return mBase.getApplicationScale();
-    }
 }
diff --git a/core/java/android/content/Entity.java b/core/java/android/content/Entity.java
index 31ea2cd..325dce5 100644
--- a/core/java/android/content/Entity.java
+++ b/core/java/android/content/Entity.java
@@ -17,10 +17,88 @@
 package android.content;
 
 import android.os.Parcelable;
+import android.os.Parcel;
+import android.net.Uri;
+import android.util.Log;
+
+import java.util.ArrayList;
 
 /**
  * Objects that pass through the ContentProvider and ContentResolver's methods that deal with
  * Entities must implement this abstract base class and thus themselves be Parcelable.
  */
-public abstract class Entity implements Parcelable {
+public final class Entity implements Parcelable {
+    final private ContentValues mValues;
+    final private ArrayList<NamedContentValues> mSubValues;
+
+    public Entity(ContentValues values) {
+        mValues = values;
+        mSubValues = new ArrayList<NamedContentValues>();
+    }
+
+    public ContentValues getEntityValues() {
+        return mValues;
+    }
+
+    public ArrayList<NamedContentValues> getSubValues() {
+        return mSubValues;
+    }
+
+    public void addSubValue(Uri uri, ContentValues values) {
+        mSubValues.add(new Entity.NamedContentValues(uri, values));
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        mValues.writeToParcel(dest, 0);
+        dest.writeInt(mSubValues.size());
+        for (NamedContentValues value : mSubValues) {
+            value.uri.writeToParcel(dest, 0);
+            value.values.writeToParcel(dest, 0);
+        }
+    }
+
+    private Entity(Parcel source) {
+        mValues = ContentValues.CREATOR.createFromParcel(source);
+        final int numValues = source.readInt();
+        mSubValues = new ArrayList<NamedContentValues>(numValues);
+        for (int i = 0; i < numValues; i++) {
+            final Uri uri = Uri.CREATOR.createFromParcel(source);
+            final ContentValues values = ContentValues.CREATOR.createFromParcel(source);
+            mSubValues.add(new NamedContentValues(uri, values));
+        }
+    }
+
+    public static final Creator<Entity> CREATOR = new Creator<Entity>() {
+        public Entity createFromParcel(Parcel source) {
+            return new Entity(source);
+        }
+
+        public Entity[] newArray(int size) {
+            return new Entity[size];
+        }
+    };
+
+    public static class NamedContentValues {
+        public final Uri uri;
+        public final ContentValues values;
+
+        public NamedContentValues(Uri uri, ContentValues values) {
+            this.uri = uri;
+            this.values = values;
+        }
+    }
+
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("Entity: ").append(getEntityValues());
+        for (Entity.NamedContentValues namedValue : getSubValues()) {
+            sb.append("\n  ").append(namedValue.uri);
+            sb.append("\n  -> ").append(namedValue.values);
+        }
+        return sb.toString();
+    }
 }
diff --git a/core/java/android/content/EntityIterator.java b/core/java/android/content/EntityIterator.java
index 61914cf..5e5f14c 100644
--- a/core/java/android/content/EntityIterator.java
+++ b/core/java/android/content/EntityIterator.java
@@ -18,7 +18,7 @@
 
 import android.os.RemoteException;
 
-public interface  EntityIterator {
+public interface EntityIterator {
     /**
      * Returns whether there are more elements to iterate, i.e. whether the
      * iterator is positioned in front of an element.
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index f82c982..7e5aba5 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -28,6 +28,7 @@
 import android.os.ParcelFileDescriptor;
 
 import java.io.FileNotFoundException;
+import java.util.ArrayList;
 
 /**
  * The ipc interface to talk to a content provider.
@@ -60,7 +61,7 @@
             throws RemoteException, FileNotFoundException;
     public AssetFileDescriptor openAssetFile(Uri url, String mode)
             throws RemoteException, FileNotFoundException;
-    public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations)
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws RemoteException, OperationApplicationException;
 
     /* IPC constants */
diff --git a/core/java/android/content/IEntityIterator.java b/core/java/android/content/IEntityIterator.java
index 1887df2..1c478b3 100644
--- a/core/java/android/content/IEntityIterator.java
+++ b/core/java/android/content/IEntityIterator.java
@@ -21,6 +21,7 @@
 import android.os.IInterface;
 import android.os.Parcel;
 import android.os.RemoteException;
+import android.os.Parcelable;
 import android.util.Log;
 
 /**
@@ -93,7 +94,7 @@
                         return true;
                     }
                     reply.writeNoException();
-                    reply.writeParcelable(entity, 0);
+                    entity.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                     return true;
                 }
 
@@ -149,7 +150,7 @@
                     _data.writeInterfaceToken(DESCRIPTOR);
                     mRemote.transact(Stub.TRANSACTION_next, _data, _reply, 0);
                     _reply.readException();
-                    return (Entity) _reply.readParcelable(null /* classLoader */);
+                    return Entity.CREATOR.createFromParcel(_reply);
                 } finally {
                     _reply.recycle();
                     _data.recycle();
diff --git a/core/java/android/content/SyncAdapterNew.java b/core/java/android/content/SyncAdapterNew.java
new file mode 100644
index 0000000..5b23395
--- /dev/null
+++ b/core/java/android/content/SyncAdapterNew.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.*;
+import android.os.Process;
+import android.accounts.Account;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @hide
+ */
+public abstract class SyncAdapterNew {
+    private static final String TAG = "SyncAdapter";
+    private final Context mContext;
+    private final String mAuthority;
+
+    /** Kernel event log tag.  Also listed in data/etc/event-log-tags. */
+    public static final int LOG_SYNC_DETAILS = 2743;
+
+    public SyncAdapterNew(Context context, String authority) {
+        mContext = context;
+        mAuthority = authority;
+    }
+
+    class Transport extends ISyncAdapter.Stub {
+        private final AtomicInteger mNumSyncStarts = new AtomicInteger(0);
+        private volatile Thread mSyncThread;
+
+        public void startSync(ISyncContext syncContext, Account account, Bundle extras) {
+            boolean alreadyInProgress;
+            synchronized (this) {
+                if (mSyncThread == null) {
+                    mSyncThread = new Thread(
+                            new SyncRunnable(new SyncContext(syncContext), account, extras),
+                            "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet());
+                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+                    mSyncThread.start();
+                    alreadyInProgress = false;
+                } else {
+                    alreadyInProgress = true;
+                }
+            }
+
+            if (alreadyInProgress) {
+                try {
+                    syncContext.onFinished(SyncResult.ALREADY_IN_PROGRESS);
+                } catch (RemoteException e) {
+                    // don't care if the caller is no longer around
+                }
+            }
+        }
+
+        public void cancelSync() {
+            synchronized (this) {
+                if (mSyncThread != null) {
+                    mSyncThread.interrupt();
+                }
+            }
+        }
+
+        private class SyncRunnable implements Runnable {
+            private final SyncContext mSyncContext;
+            private final Account mAccount;
+            private final Bundle mExtras;
+
+            private SyncRunnable(SyncContext syncContext, Account account, Bundle extras) {
+                mSyncContext = syncContext;
+                mAccount = account;
+                mExtras = extras;
+            }
+
+            public void run() {
+                if (isCanceled()) {
+                    return;
+                }
+
+                SyncResult syncResult = new SyncResult();
+                ContentProviderClient provider = mAuthority != null
+                        ? mContext.getContentResolver().acquireContentProviderClient(mAuthority)
+                        : null;
+                try {
+                    SyncAdapterNew.this.performSync(mAccount, mExtras, provider, syncResult);
+                } finally {
+                    if (provider != null) {
+                        provider.release();
+                    }
+                    if (!isCanceled()) {
+                        mSyncContext.onFinished(syncResult);
+                    }
+                    mSyncThread = null;
+                }
+            }
+
+            private boolean isCanceled() {
+                return Thread.currentThread().isInterrupted();
+            }
+        }
+    }
+
+    Transport mTransport = new Transport();
+
+    /**
+     * Get the Transport object.
+     */
+    public final ISyncAdapter getISyncAdapter() {
+        return mTransport;
+    }
+
+    /**
+     * Perform a sync for this account. SyncAdapter-specific parameters may
+     * be specified in extras, which is guaranteed to not be null. Invocations
+     * of this method are guaranteed to be serialized.
+     *
+     * @param account the account that should be synced
+     * @param extras SyncAdapter-specific parameters
+     */
+    public abstract void performSync(Account account, Bundle extras,
+            ContentProviderClient provider, SyncResult syncResult);
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index f16eb74..f3dfc5a 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -186,7 +186,6 @@
      */
     public int uid;
     
-
     /**
      * The list of densities in DPI that application supprots. This
      * field is only set if the {@link PackageManager#GET_SUPPORTS_DENSITIES} flag was
@@ -195,6 +194,12 @@
     public int[] supportsDensities;
 
     /**
+     * True when the application's window can be expanded over default window
+     * size in target density (320x480 for 1.0 density, 480x720 for 1.5 density etc)
+     */
+    public boolean expandable = false;
+
+    /**
      * The minimum SDK version this application targets.  It may run on earilier
      * versions, but it knows how to work with any new behavior added at this
      * version.  Will be {@link android.os.Build.VERSION_CODES#CUR_DEVELOPMENT}
@@ -228,6 +233,7 @@
         pw.println(prefix + "manageSpaceActivityName="+manageSpaceActivityName);
         pw.println(prefix + "description=0x"+Integer.toHexString(descriptionRes));
         pw.println(prefix + "supportsDensities=" + supportsDensities);
+        pw.println(prefix + "expandable=" + expandable);
         super.dumpBack(pw, prefix);
     }
     
@@ -275,6 +281,7 @@
         manageSpaceActivityName = orig.manageSpaceActivityName;
         descriptionRes = orig.descriptionRes;
         supportsDensities = orig.supportsDensities;
+        expandable = orig.expandable;
     }
 
 
@@ -307,6 +314,7 @@
         dest.writeString(backupAgentName);
         dest.writeInt(descriptionRes);
         dest.writeIntArray(supportsDensities);
+        dest.writeInt(expandable ? 1 : 0);
     }
 
     public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -338,6 +346,7 @@
         backupAgentName = source.readString();
         descriptionRes = source.readInt();
         supportsDensities = source.createIntArray();
+        expandable = source.readInt() != 0;
     }
 
     /**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 6d18933..9eca4a5 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -168,7 +168,12 @@
 
     void addPreferredActivity(in IntentFilter filter, int match,
             in ComponentName[] set, in ComponentName activity);
+
+    void replacePreferredActivity(in IntentFilter filter, int match,
+            in ComponentName[] set, in ComponentName activity);
+
     void clearPackagePreferredActivities(String packageName);
+
     int getPreferredActivities(out List<IntentFilter> outFilters,
             out List<ComponentName> outActivities, String packageName);
     
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 147968b..88eccf7 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -180,6 +180,12 @@
     public static final int MATCH_DEFAULT_ONLY   = 0x00010000;
 
     /**
+     * {@link ApplicationInfo} flag: return the
+     * {link ApplicationInfo#expandable} boolean flag of the package.
+     */
+    public static final int GET_EXPANDABLE = 0x00020000;
+
+    /**
      * Permission check result: this is returned by {@link #checkPermission}
      * if the permission has been granted to the given package.
      */
@@ -1628,6 +1634,26 @@
             ComponentName[] set, ComponentName activity);
 
     /**
+     * Replaces an existing preferred activity mapping to the system, and if that were not present
+     * adds a new preferred activity.  This will be used
+     * to automatically select the given activity component when
+     * {@link Context#startActivity(Intent) Context.startActivity()} finds
+     * multiple matching activities and also matches the given filter.
+     *
+     * @param filter The set of intents under which this activity will be
+     * made preferred.
+     * @param match The IntentFilter match category that this preference
+     * applies to.
+     * @param set The set of activities that the user was picking from when
+     * this preference was made.
+     * @param activity The component name of the activity that is to be
+     * preferred.
+     * @hide
+     */
+    public abstract void replacePreferredActivity(IntentFilter filter, int match,
+            ComponentName[] set, ComponentName activity);
+
+    /**
      * Remove all preferred activity mappings, previously added with
      * {@link #addPreferredActivity}, from the
      * system whose activities are implemented in the given package name.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 212b590..e2c0fe6 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -835,6 +835,26 @@
                     + parser.getName();
                 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                 return null;
+
+
+            } else if (tagName.equals("supports-density")) {
+                sa = res.obtainAttributes(attrs,
+                        com.android.internal.R.styleable.AndroidManifestSupportsDensity);
+
+                int density = sa.getInteger(
+                        com.android.internal.R.styleable.AndroidManifestSupportsDensity_density, -1);
+
+                sa.recycle();
+
+                if (density != -1 && !pkg.supportsDensityList.contains(density)) {
+                    pkg.supportsDensityList.add(density);
+                }
+
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (tagName.equals("expandable")) {
+                pkg.expandable = true;
+                XmlUtils.skipCurrentTag(parser);
             } else {
                 Log.w(TAG, "Bad element under <manifest>: "
                       + parser.getName());
@@ -866,7 +886,8 @@
             pkg.usesLibraryFiles = new String[pkg.usesLibraries.size()];
             pkg.usesLibraries.toArray(pkg.usesLibraryFiles);
         }
-
+        // TODO: enable all density & expandable if target sdk is higher than donut 
+        
         int size = pkg.supportsDensityList.size();
         if (size > 0) {
             int densities[] = pkg.supportsDensities = new int[size];
@@ -1345,21 +1366,6 @@
 
                 XmlUtils.skipCurrentTag(parser);
 
-            } else if (tagName.equals("supports-density")) {
-                sa = res.obtainAttributes(attrs,
-                        com.android.internal.R.styleable.AndroidManifestSupportsDensity);
-
-                int density = sa.getInteger(
-                        com.android.internal.R.styleable.AndroidManifestSupportsDensity_density, -1);
-
-                sa.recycle();
-
-                if (density != -1 && !owner.supportsDensityList.contains(density)) {
-                    owner.supportsDensityList.add(density);
-                }
-
-                XmlUtils.skipCurrentTag(parser);
-
             } else {
                 if (!RIGID_PARSER) {
                     Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
@@ -2244,6 +2250,9 @@
         public final ArrayList<Integer> supportsDensityList = new ArrayList<Integer>();
         public int[] supportsDensities = null;
 
+        // If the application's window is expandable.
+        public boolean expandable;
+        
         // If this is a 3rd party app, this is the path of the zip file.
         public String mPath;
 
@@ -2415,7 +2424,10 @@
             return true;
         }
         if ((flags & PackageManager.GET_SUPPORTS_DENSITIES) != 0
-            && p.supportsDensities != null) {
+                && p.supportsDensities != null) {
+            return true;
+        }
+        if ((flags & PackageManager.GET_EXPANDABLE) != 0) {
             return true;
         }
         return false;
@@ -2438,6 +2450,9 @@
         if ((flags & PackageManager.GET_SUPPORTS_DENSITIES) != 0) {
             ai.supportsDensities = p.supportsDensities;
         }
+        if ((flags & PackageManager.GET_EXPANDABLE) != 0) {
+            ai.expandable = p.expandable;
+        }
         return ai;
     }
 
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index 231e3e2..a37e4e8 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -16,6 +16,7 @@
 
 package android.content.res;
 
+import android.os.MemoryFile;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
@@ -24,6 +25,8 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.FileChannel;
 
 /**
  * File descriptor of an entry in the AssetManager.  This provides your own
@@ -124,6 +127,13 @@
     }
     
     /**
+     * Checks whether this file descriptor is for a memory file.
+     */
+    private boolean isMemoryFile() throws IOException {
+        return MemoryFile.isMemoryFile(mFd.getFileDescriptor());
+    }
+
+    /**
      * Create and return a new auto-close input stream for this asset.  This
      * will either return a full asset {@link AutoCloseInputStream}, or
      * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream
@@ -132,6 +142,12 @@
      * should only call this once for a particular asset.
      */
     public FileInputStream createInputStream() throws IOException {
+        if (isMemoryFile()) {
+            if (mLength > Integer.MAX_VALUE) {
+                throw new IOException("File length too large for a memory file: " + mLength);
+            }
+            return new AutoCloseMemoryFileInputStream(mFd, (int)mLength);
+        }
         if (mLength < 0) {
             return new ParcelFileDescriptor.AutoCloseInputStream(mFd);
         }
@@ -262,6 +278,66 @@
     }
     
     /**
+     * An input stream that reads from a MemoryFile and closes it when the stream is closed.
+     * This extends FileInputStream just because {@link #createInputStream} returns
+     * a FileInputStream. All the FileInputStream methods are
+     * overridden to use the MemoryFile instead.
+     */
+    private static class AutoCloseMemoryFileInputStream extends FileInputStream {
+        private ParcelFileDescriptor mParcelFd;
+        private MemoryFile mFile;
+        private InputStream mStream;
+
+        public AutoCloseMemoryFileInputStream(ParcelFileDescriptor fd, int length)
+                throws IOException {
+            super(fd.getFileDescriptor());
+            mParcelFd = fd;
+            mFile = new MemoryFile(fd.getFileDescriptor(), length, "r");
+            mStream = mFile.getInputStream();
+        }
+
+        @Override
+        public int available() throws IOException {
+            return mStream.available();
+        }
+
+        @Override
+        public void close() throws IOException {
+            mParcelFd.close();  // must close ParcelFileDescriptor, not just the file descriptor,
+                                // since it could be a subclass of ParcelFileDescriptor.
+                                // E.g. ContentResolver.ParcelFileDescriptorInner.close() releases
+                                // a content provider
+            mFile.close();      // to unmap the memory file from the address space.
+            mStream.close();    // doesn't actually do anything
+        }
+
+        @Override
+        public FileChannel getChannel() {
+            return null;
+        }
+
+        @Override
+        public int read() throws IOException {
+            return mStream.read();
+        }
+
+        @Override
+        public int read(byte[] buffer, int offset, int count) throws IOException {
+            return mStream.read(buffer, offset, count);
+        }
+
+        @Override
+        public int read(byte[] buffer) throws IOException {
+            return mStream.read(buffer);
+        }
+
+        @Override
+        public long skip(long count) throws IOException {
+            return mStream.skip(count);
+        }
+    }
+
+    /**
      * An OutputStream you can create on a ParcelFileDescriptor, which will
      * take care of calling {@link ParcelFileDescriptor#close
      * ParcelFileDescritor.close()} for you when the stream is closed.
@@ -345,4 +421,16 @@
             return new AssetFileDescriptor[size];
         }
     };
+
+    /**
+     * Creates an AssetFileDescriptor from a memory file.
+     *
+     * @hide
+     */
+    public static AssetFileDescriptor fromMemoryFile(MemoryFile memoryFile)
+            throws IOException {
+        ParcelFileDescriptor fd = memoryFile.getParcelFileDescriptor();
+        return new AssetFileDescriptor(fd, 0, memoryFile.length());
+    }
+
 }
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
new file mode 100644
index 0000000..19379fb
--- /dev/null
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res;
+
+import android.content.pm.ApplicationInfo;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+
+/**
+ * CompatibilityInfo class keeps the information about compatibility mode that the application is
+ * running under.
+ * 
+ *  {@hide} 
+ */
+public class CompatibilityInfo {
+    /** default compatibility info object for compatible applications */
+    public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo(); 
+
+    /**
+     * The default width of the screen in portrait mode. 
+     */
+    public static final int DEFAULT_PORTRAIT_WIDTH = 320;
+
+    /**
+     * The default height of the screen in portrait mode. 
+     */    
+    public static final int DEFAULT_PORTRAIT_HEIGHT = 480;
+
+    /**
+     * Application's scale.
+     */
+    public final float mApplicationScale;
+
+    /**
+     * Application's inverted scale.
+     */
+    public final float mApplicationInvertedScale;
+    
+    /**
+     * 
+     * A boolean flag to indicates that the application can expand over the original size.
+     */
+    public final boolean mExpandable;
+    
+    /**
+     * A boolean flag to tell if the application needs scaling (when mApplicationScale != 1.0f)
+     */
+    public final boolean mScalingRequired;
+
+    public CompatibilityInfo(ApplicationInfo appInfo) {
+        // A temp workaround to fix rotation issue.
+        // mExpandable = appInfo.expandable;
+        mExpandable = true;
+        float packageDensityScale = -1.0f;
+        if (appInfo.supportsDensities != null) {
+            int minDiff = Integer.MAX_VALUE;
+            for (int density : appInfo.supportsDensities) {
+                int tmpDiff = Math.abs(DisplayMetrics.DEVICE_DENSITY - density);
+                if (tmpDiff == 0) {
+                    packageDensityScale = 1.0f;
+                    break;
+                }
+                // prefer higher density (appScale>1.0), unless that's only option.
+                if (tmpDiff < minDiff && packageDensityScale < 1.0f) {
+                    packageDensityScale = DisplayMetrics.DEVICE_DENSITY / (float) density;
+                    minDiff = tmpDiff;
+                }
+            }
+        }
+        if (packageDensityScale > 0.0f) {
+            mApplicationScale = packageDensityScale;
+        } else {
+            mApplicationScale = DisplayMetrics.DEVICE_DENSITY / (float) DisplayMetrics.DEFAULT_DENSITY;
+        }
+        mApplicationInvertedScale = 1.0f / mApplicationScale;
+        mScalingRequired = mApplicationScale != 1.0f;
+    }
+
+    private CompatibilityInfo() {
+        mApplicationScale = mApplicationInvertedScale = 1.0f;
+        mExpandable = true;
+        mScalingRequired = false;
+    }
+
+    @Override
+    public String toString() {
+        return "CompatibilityInfo{scale=" + mApplicationScale +
+                ", expandable=" + mExpandable + "}"; 
+    }
+}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 665e40c..976b618 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -22,6 +22,8 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.app.ActivityThread.PackageInfo;
+import android.content.pm.ApplicationInfo;
 import android.graphics.Movie;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.BitmapDrawable;
@@ -84,7 +86,9 @@
     private final Configuration mConfiguration = new Configuration();
     /*package*/ final DisplayMetrics mMetrics = new DisplayMetrics();
     PluralRules mPluralRule;
-
+    
+    private final CompatibilityInfo mCompatibilityInfo;
+    
     private static final SparseArray<Object> EMPTY_ARRAY = new SparseArray<Object>() {
         @Override
         public void put(int k, Object o) {
@@ -126,23 +130,36 @@
      */
     public Resources(AssetManager assets, DisplayMetrics metrics,
             Configuration config) {
-        this(assets, metrics, config, true);
+        this(assets, metrics, config, null);
     }
 
     /**
-     * Create a resource with an additional flag for preloaded
-     * drawable cache. Used by {@link ActivityThread}.
-     *
+     * Creates a new Resources object with ApplicationInfo.
+     * 
+     * @param assets Previously created AssetManager. 
+     * @param metrics Current display metrics to consider when 
+     *                selecting/computing resource values.
+     * @param config Desired device configuration to consider when 
+     *               selecting/computing resource values (optional).
+     * @param appInfo this resource's application info.
      * @hide
      */
     public Resources(AssetManager assets, DisplayMetrics metrics,
-        Configuration config, boolean usePreloadedCache) {
+            Configuration config, ApplicationInfo appInfo) {
         mAssets = assets;
         mConfiguration.setToDefaults();
         mMetrics.setToDefaults();
+        if (appInfo != null) {
+            mCompatibilityInfo = new CompatibilityInfo(appInfo);
+            if (DEBUG_CONFIG) {
+                Log.d(TAG, "compatibility for " + appInfo.packageName + " : " + mCompatibilityInfo);
+            }
+        } else {
+            mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+        }
         updateConfiguration(config, metrics);
         assets.ensureStringBlocks();
-        if (usePreloadedCache) {
+        if (!mCompatibilityInfo.mScalingRequired) {
             mPreloadedDrawables = sPreloadedDrawables;
         } else {
             mPreloadedDrawables = emptySparseArray();
@@ -1251,6 +1268,7 @@
             }
             if (metrics != null) {
                 mMetrics.setTo(metrics);
+                mMetrics.updateMetrics(mCompatibilityInfo, mConfiguration);
             }
             mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
 
@@ -1356,6 +1374,17 @@
     public Configuration getConfiguration() {
         return mConfiguration;
     }
+    
+    /**
+     * Return the compatibility mode information for the application.
+     * The returned object should be treated as read-only.
+     * 
+     * @return compatibility info. null if the app does not require compatibility mode.
+     * @hide
+     */
+    public CompatibilityInfo getCompatibilityInfo() {
+        return mCompatibilityInfo;
+    }
 
     /**
      * Return a resource identifier for the given resource name.  A fully
@@ -1920,5 +1949,6 @@
         updateConfiguration(null, null);
         mAssets.ensureStringBlocks();
         mPreloadedDrawables = sPreloadedDrawables;
+        mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
     }
 }
diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java
index 4ac0aef..27a02e2 100644
--- a/core/java/android/database/AbstractWindowedCursor.java
+++ b/core/java/android/database/AbstractWindowedCursor.java
@@ -166,6 +166,48 @@
         return mWindow.isBlob(mPos, columnIndex);
     }
 
+    public boolean isString(int columnIndex)
+    {
+        checkPosition();
+
+        synchronized(mUpdatedRows) {
+            if (isFieldUpdated(columnIndex)) {
+                Object object = getUpdatedField(columnIndex);
+                return object == null || object instanceof String;
+            }
+        }
+
+        return mWindow.isString(mPos, columnIndex);
+    }
+
+    public boolean isLong(int columnIndex)
+    {
+        checkPosition();
+
+        synchronized(mUpdatedRows) {
+            if (isFieldUpdated(columnIndex)) {
+                Object object = getUpdatedField(columnIndex);
+                return object != null && (object instanceof Integer || object instanceof Long);
+            }
+        }
+
+        return mWindow.isLong(mPos, columnIndex);
+    }
+
+    public boolean isFloat(int columnIndex)
+    {
+        checkPosition();
+
+        synchronized(mUpdatedRows) {
+            if (isFieldUpdated(columnIndex)) {
+                Object object = getUpdatedField(columnIndex);
+                return object != null && (object instanceof Float || object instanceof Double);
+            }
+        }
+
+        return mWindow.isFloat(mPos, columnIndex);
+    }
+
     @Override
     protected void checkPosition()
     {
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 8e26730..99db81b 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -263,7 +263,58 @@
         }
     }
 
+    /**
+     * Checks if a field contains a long
+     *
+     * @param row the row to read from, row - getStartPosition() being the actual row in the window
+     * @param col the column to read from
+     * @return {@code true} if given field is a long
+     */
+    public boolean isLong(int row, int col) {
+        acquireReference();
+        try {
+            return isInteger_native(row - mStartPos, col);
+        } finally {
+            releaseReference();
+        }
+    }
+
+    /**
+     * Checks if a field contains a float.
+     *
+     * @param row the row to read from, row - getStartPosition() being the actual row in the window
+     * @param col the column to read from
+     * @return {@code true} if given field is a float
+     */
+    public boolean isFloat(int row, int col) {
+        acquireReference();
+        try {
+            return isFloat_native(row - mStartPos, col);
+        } finally {
+            releaseReference();
+        }
+    }
+
+    /**
+     * Checks if a field contains either a String or is null.
+     *
+     * @param row the row to read from, row - getStartPosition() being the actual row in the window
+     * @param col the column to read from
+     * @return {@code true} if given field is {@code NULL} or a String
+     */
+    public boolean isString(int row, int col) {
+        acquireReference();
+        try {
+            return isString_native(row - mStartPos, col);
+        } finally {
+            releaseReference();
+        }
+    }
+
     private native boolean isBlob_native(int row, int col);
+    private native boolean isString_native(int row, int col);
+    private native boolean isInteger_native(int row, int col);
+    private native boolean isFloat_native(int row, int col);
 
     /**
      * Returns a String for the given field.
diff --git a/core/java/android/database/sqlite/SQLiteContentHelper.java b/core/java/android/database/sqlite/SQLiteContentHelper.java
new file mode 100644
index 0000000..2800d86
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteContentHelper.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.os.MemoryFile;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * Some helper functions for using SQLite database to implement content providers.
+ *
+ * @hide
+ */
+public class SQLiteContentHelper {
+
+    /**
+     * Runs an SQLite query and returns an AssetFileDescriptor for the
+     * blob in column 0 of the first row. If the first column does
+     * not contain a blob, an unspecified exception is thrown.
+     *
+     * @param db Handle to a readable database.
+     * @param sql SQL query, possibly with query arguments.
+     * @param selectionArgs Query argument values, or {@code null} for no argument.
+     * @return If no exception is thrown, a non-null AssetFileDescriptor is returned.
+     * @throws FileNotFoundException If the query returns no results or the
+     *         value of column 0 is NULL, or if there is an error creating the
+     *         asset file descriptor.
+     */
+    public static AssetFileDescriptor getBlobColumnAsAssetFile(SQLiteDatabase db, String sql,
+            String[] selectionArgs) throws FileNotFoundException {
+        try {
+            MemoryFile file = simpleQueryForBlobMemoryFile(db, sql, selectionArgs);
+            if (file == null) {
+                throw new FileNotFoundException("No results.");
+            }
+            return AssetFileDescriptor.fromMemoryFile(file);
+        } catch (IOException ex) {
+            throw new FileNotFoundException(ex.toString());
+        }
+    }
+
+    /**
+     * Runs an SQLite query and returns a MemoryFile for the
+     * blob in column 0 of the first row. If the first column does
+     * not contain a blob, an unspecified exception is thrown.
+     *
+     * @return A memory file, or {@code null} if the query returns no results
+     *         or the value column 0 is NULL.
+     * @throws IOException If there is an error creating the memory file.
+     */
+    // TODO: make this native and use the SQLite blob API to reduce copying
+    private static MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql,
+            String[] selectionArgs) throws IOException {
+        Cursor cursor = db.rawQuery(sql, selectionArgs);
+        if (cursor == null) {
+            return null;
+        }
+        try {
+            if (!cursor.moveToFirst()) {
+                return null;
+            }
+            byte[] bytes = cursor.getBlob(0);
+            if (bytes == null) {
+                return null;
+            }
+            MemoryFile file = new MemoryFile(null, bytes.length);
+            file.writeBytes(bytes, 0, 0, bytes.length);
+            file.deactivate();
+            return file;
+        } finally {
+            cursor.close();
+        }
+    }
+
+}
diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java
index 227cf3d..6f2c2a7 100755
--- a/core/java/android/gesture/GestureOverlayView.java
+++ b/core/java/android/gesture/GestureOverlayView.java
@@ -106,6 +106,9 @@
     // TODO: Make this a list of WeakReferences
     private final ArrayList<OnGesturePerformedListener> mOnGesturePerformedListeners =
             new ArrayList<OnGesturePerformedListener>();
+    // TODO: Make this a list of WeakReferences
+    private final ArrayList<OnGesturingListener> mOnGesturingListeners =
+            new ArrayList<OnGesturingListener>();
 
     private boolean mHandleGestureActions;
 
@@ -319,6 +322,18 @@
         mHandleGestureActions = false;
     }
 
+    public void addOnGesturingListener(OnGesturingListener listener) {
+        mOnGesturingListeners.add(listener);
+    }
+
+    public void removeOnGesturingListener(OnGesturingListener listener) {
+        mOnGesturingListeners.remove(listener);
+    }
+
+    public void removeAllOnGesturingListeners() {
+        mOnGesturingListeners.clear();
+    }
+
     public boolean isGesturing() {
         return mIsGesturing;
     }
@@ -401,7 +416,7 @@
                 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
 
         final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
-        final int count = listeners.size();
+        int count = listeners.size();
         for (int i = 0; i < count; i++) {
             listeners.get(i).onGestureCancelled(this, event);
         }
@@ -411,6 +426,12 @@
         clear(false);
         mIsGesturing = false;
         mStrokeBuffer.clear();
+
+        final ArrayList<OnGesturingListener> otherListeners = mOnGesturingListeners;
+        count = otherListeners.size();
+        for (int i = 0; i < count; i++) {
+            otherListeners.get(i).onGesturingEnded(this);
+        }
     }
 
     @Override
@@ -577,6 +598,12 @@
 
                         mIsGesturing = true;
                         setCurrentColor(mCertainGestureColor);
+
+                        final ArrayList<OnGesturingListener> listeners = mOnGesturingListeners;
+                        int count = listeners.size();
+                        for (int i = 0; i < count; i++) {
+                            listeners.get(i).onGesturingStarted(this);
+                        }
                     }
                 }
             }
@@ -621,6 +648,12 @@
 
         mStrokeBuffer.clear();
         mIsGesturing = false;
+
+        final ArrayList<OnGesturingListener> listeners = mOnGesturingListeners;
+        int count = listeners.size();
+        for (int i = 0; i < count; i++) {
+            listeners.get(i).onGesturingEnded(this);
+        }
     }
 
     private void cancelGesture(MotionEvent event) {
@@ -635,12 +668,10 @@
     }
 
     private void fireOnGesturePerformed() {
-        final ArrayList<OnGesturePerformedListener> actionListeners =
-                mOnGesturePerformedListeners;
+        final ArrayList<OnGesturePerformedListener> actionListeners = mOnGesturePerformedListeners;
         final int count = actionListeners.size();
         for (int i = 0; i < count; i++) {
-            actionListeners.get(i).onGesturePerformed(GestureOverlayView.this,
-                    mCurrentGesture);
+            actionListeners.get(i).onGesturePerformed(GestureOverlayView.this, mCurrentGesture);
         }
     }
 
@@ -683,6 +714,12 @@
         }
     }
 
+    public static interface OnGesturingListener {
+        void onGesturingStarted(GestureOverlayView overlay);
+
+        void onGesturingEnded(GestureOverlayView overlay);
+    }
+
     public static interface OnGestureListener {
         void onGestureStarted(GestureOverlayView overlay, MotionEvent event);
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 358a546..528def5 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -307,6 +307,13 @@
              * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
              */
             public abstract int getStarts(int which);
+
+            /**
+             * Returns the cpu time spent in microseconds while the process was in the foreground.
+             * @param which one of STATS_TOTAL, STATS_LAST, STATS_CURRENT or STATS_UNPLUGGED
+             * @return foreground cpu time in microseconds
+             */
+            public abstract long getForegroundTime(int which);
         }
 
         /**
@@ -364,7 +371,7 @@
     public abstract int getStartCount();
     
     /**
-     * Returns the time in milliseconds that the screen has been on while the device was
+     * Returns the time in microseconds that the screen has been on while the device was
      * running on battery.
      * 
      * {@hide}
@@ -384,7 +391,7 @@
     public static final int NUM_SCREEN_BRIGHTNESS_BINS = 5;
     
     /**
-     * Returns the time in milliseconds that the screen has been on with
+     * Returns the time in microseconds that the screen has been on with
      * the given brightness
      * 
      * {@hide}
@@ -395,7 +402,7 @@
     public abstract int getInputEventCount(int which);
     
     /**
-     * Returns the time in milliseconds that the phone has been on while the device was
+     * Returns the time in microseconds that the phone has been on while the device was
      * running on battery.
      * 
      * {@hide}
@@ -415,7 +422,7 @@
     public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
     
     /**
-     * Returns the time in milliseconds that the phone has been running with
+     * Returns the time in microseconds that the phone has been running with
      * the given signal strength.
      * 
      * {@hide}
@@ -443,7 +450,7 @@
     public static final int NUM_DATA_CONNECTION_TYPES = 5;
     
     /**
-     * Returns the time in milliseconds that the phone has been running with
+     * Returns the time in microseconds that the phone has been running with
      * the given data connection.
      * 
      * {@hide}
@@ -460,7 +467,7 @@
     public abstract int getPhoneDataConnectionCount(int dataType, int which);
 
     /**
-     * Returns the time in milliseconds that wifi has been on while the device was
+     * Returns the time in microseconds that wifi has been on while the device was
      * running on battery.
      * 
      * {@hide}
@@ -468,7 +475,7 @@
     public abstract long getWifiOnTime(long batteryRealtime, int which);
 
     /**
-     * Returns the time in milliseconds that wifi has been on and the driver has
+     * Returns the time in microseconds that wifi has been on and the driver has
      * been in the running state while the device was running on battery.
      *
      * {@hide}
@@ -476,7 +483,7 @@
     public abstract long getWifiRunningTime(long batteryRealtime, int which);
 
     /**
-     * Returns the time in milliseconds that bluetooth has been on while the device was
+     * Returns the time in microseconds that bluetooth has been on while the device was
      * running on battery.
      * 
      * {@hide}
diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java
index 65e83c7..c14925c 100644
--- a/core/java/android/os/MemoryFile.java
+++ b/core/java/android/os/MemoryFile.java
@@ -37,9 +37,14 @@
 {
     private static String TAG = "MemoryFile";
 
+    // mmap(2) protection flags from <sys/mman.h>
+    private static final int PROT_READ = 0x1;
+    private static final int PROT_WRITE = 0x2;
+
     private static native FileDescriptor native_open(String name, int length) throws IOException;
     // returns memory address for ashmem region
-    private static native int native_mmap(FileDescriptor fd, int length) throws IOException;
+    private static native int native_mmap(FileDescriptor fd, int length, int mode)
+            throws IOException;
     private static native void native_munmap(int addr, int length) throws IOException;
     private static native void native_close(FileDescriptor fd);
     private static native int native_read(FileDescriptor fd, int address, byte[] buffer,
@@ -47,14 +52,16 @@
     private static native void native_write(FileDescriptor fd, int address, byte[] buffer,
             int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;
     private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException;
+    private static native boolean native_is_ashmem_region(FileDescriptor fd) throws IOException;
 
     private FileDescriptor mFD;        // ashmem file descriptor
     private int mAddress;   // address of ashmem memory
     private int mLength;    // total length of our ashmem region
     private boolean mAllowPurging = false;  // true if our ashmem region is unpinned
+    private final boolean mOwnsRegion;  // false if this is a ref to an existing ashmem region
 
     /**
-     * MemoryFile constructor.
+     * Allocates a new ashmem region. The region is initially not purgable.
      *
      * @param name optional name for the file (can be null).
      * @param length of the memory file in bytes.
@@ -63,11 +70,43 @@
     public MemoryFile(String name, int length) throws IOException {
         mLength = length;
         mFD = native_open(name, length);
-        mAddress = native_mmap(mFD, length);
+        mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
+        mOwnsRegion = true;
     }
 
     /**
-     * Closes and releases all resources for the memory file.
+     * Creates a reference to an existing memory file. Changes to the original file
+     * will be available through this reference.
+     * Calls to {@link #allowPurging(boolean)} on the returned MemoryFile will fail.
+     *
+     * @param fd File descriptor for an existing memory file, as returned by
+     *        {@link #getFileDescriptor()}. This file descriptor will be closed
+     *        by {@link #close()}.
+     * @param length Length of the memory file in bytes.
+     * @param mode File mode. Currently only "r" for read-only access is supported.
+     * @throws NullPointerException if <code>fd</code> is null.
+     * @throws IOException If <code>fd</code> does not refer to an existing memory file,
+     *         or if the file mode of the existing memory file is more restrictive
+     *         than <code>mode</code>.
+     *
+     * @hide
+     */
+    public MemoryFile(FileDescriptor fd, int length, String mode) throws IOException {
+        if (fd == null) {
+            throw new NullPointerException("File descriptor is null.");
+        }
+        if (!isMemoryFile(fd)) {
+            throw new IllegalArgumentException("Not a memory file.");
+        }
+        mLength = length;
+        mFD = fd;
+        mAddress = native_mmap(mFD, length, modeToProt(mode));
+        mOwnsRegion = false;
+    }
+
+    /**
+     * Closes the memory file. If there are no other open references to the memory
+     * file, it will be deleted.
      */
     public void close() {
         deactivate();
@@ -76,7 +115,14 @@
         }
     }
 
-    private void deactivate() {
+    /**
+     * Unmaps the memory file from the process's memory space, but does not close it.
+     * After this method has been called, read and write operations through this object
+     * will fail, but {@link #getFileDescriptor()} will still return a valid file descriptor.
+     *
+     * @hide
+     */
+    public void deactivate() {
         if (!isDeactivated()) {
             try {
                 native_munmap(mAddress, mLength);
@@ -135,6 +181,9 @@
      * @return previous value of allowPurging
      */
     synchronized public boolean allowPurging(boolean allowPurging) throws IOException {
+        if (!mOwnsRegion) {
+            throw new IOException("Only the owner can make ashmem regions purgable.");
+        }
         boolean oldValue = mAllowPurging;
         if (oldValue != allowPurging) {
             native_pin(mFD, !allowPurging);
@@ -210,6 +259,64 @@
         native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
     }
 
+    /**
+     * Gets a ParcelFileDescriptor for the memory file. See {@link #getFileDescriptor()}
+     * for caveats. This must be here to allow classes outside <code>android.os</code< to
+     * make ParcelFileDescriptors from MemoryFiles, as
+     * {@link ParcelFileDescriptor#ParcelFileDescriptor(FileDescriptor)} is package private.
+     *
+     *
+     * @return The file descriptor owned by this memory file object.
+     *         The file descriptor is not duplicated.
+     * @throws IOException If the memory file has been closed.
+     *
+     * @hide
+     */
+    public ParcelFileDescriptor getParcelFileDescriptor() throws IOException {
+        return new ParcelFileDescriptor(getFileDescriptor());
+    }
+
+    /**
+     * Gets a FileDescriptor for the memory file. Note that this file descriptor
+     * is only safe to pass to {@link #MemoryFile(FileDescriptor,int)}). It
+     * should not be used with file descriptor operations that expect a file descriptor
+     * for a normal file.
+     *
+     * The returned file descriptor is not duplicated.
+     *
+     * @throws IOException If the memory file has been closed.
+     *
+     * @hide
+     */
+    public FileDescriptor getFileDescriptor() throws IOException {
+        return mFD;
+    }
+
+    /**
+     * Checks whether the given file descriptor refers to a memory file.
+     *
+     * @throws IOException If <code>fd</code> is not a valid file descriptor.
+     *
+     * @hide
+     */
+    public static boolean isMemoryFile(FileDescriptor fd) throws IOException {
+        return native_is_ashmem_region(fd);
+    }
+
+    /**
+     * Converts a file mode string to a <code>prot</code> value as expected by
+     * native_mmap().
+     *
+     * @throws IllegalArgumentException if the file mode is invalid.
+     */
+    private static int modeToProt(String mode) {
+        if ("r".equals(mode)) {
+            return PROT_READ;
+        } else {
+            throw new IllegalArgumentException("Unsupported file mode: '" + mode + "'");
+        }
+    }
+
     private class MemoryInputStream extends InputStream {
 
         private int mMark = 0;
@@ -246,13 +353,22 @@
             }
             int result = read(mSingleByte, 0, 1);
             if (result != 1) {
-                throw new IOException("read() failed");
+                return -1;
             }
             return mSingleByte[0];
         }
 
         @Override
         public int read(byte buffer[], int offset, int count) throws IOException {
+            if (offset < 0 || count < 0 || offset + count > buffer.length) {
+                // readBytes() also does this check, but we need to do it before
+                // changing count.
+                throw new IndexOutOfBoundsException();
+            }
+            count = Math.min(count, available());
+            if (count < 1) {
+                return -1;
+            }
             int result = readBytes(buffer, mOffset, offset, count);
             if (result > 0) {
                 mOffset += result;
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index cc71547..f32ab00 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -31,6 +31,75 @@
     /** A content:// style uri to the authority for the contacts provider */
     public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
 
+    public interface AccountsColumns {
+        /**
+         * The name of this account data
+         * <P>Type: TEXT</P>
+         */
+        public static final String NAME = "name";
+        /**
+         * The name of this account data
+         * <P>Type: TEXT</P>
+         */
+        public static final String TYPE = "type";
+        /**
+         * The name of this account data
+         * <P>Type: TEXT</P>
+         */
+        public static final String DATA1 = "data1";
+
+        /**
+         * The value for this account data
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATA2 = "data2";
+
+        /**
+         * The value for this account data
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATA3 = "data3";
+
+        /**
+         * The value for this account data
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATA4 = "data4";
+
+        /**
+         * The value for this account data
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATA5 = "data5";
+    }
+
+    /**
+     * Constants for the aggregates table, which contains a record per group
+     * of contact representing the same person.
+     */
+    public static final class Accounts implements BaseColumns, AccountsColumns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private Accounts()  {}
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "accounts");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * account data.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contacts_account";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a account
+         */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contacts_account";
+    }
+
     public interface AggregatesColumns {
         /**
          * The display name for the contact.
@@ -136,6 +205,11 @@
         private Contacts()  {}
 
         /**
+         * A reference to the {@link Accounts#_ID} that this data belongs to.
+         */
+        public static final String ACCOUNTS_ID = "accounts_id";
+
+        /**
          * A reference to the {@link Aggregates#_ID} that this data belongs to.
          */
         public static final String AGGREGATE_ID = "aggregate_id";
@@ -167,6 +241,22 @@
         public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/person";
 
         /**
+         * A string that uniquely identifies this contact to its source, which is referred to
+         * by the {@link #ACCOUNTS_ID}
+         */
+        public static final String SOURCE_ID = "sourceid";
+
+        /**
+         * An integer that is updated whenever this contact or its data changes.
+         */
+        public static final String VERSION = "version";
+
+        /**
+         * Set to 1 whenever the version changes
+         */
+        public static final String DIRTY = "dirty";
+
+        /**
          * A sub-directory of a single contact that contains all of their {@link Data} rows.
          * To access this directory append
          */
@@ -200,6 +290,19 @@
          */
         public static final String CONTACT_ID = "contact_id";
 
+        /**
+         * Whether this is the primary entry of its kind for the contact it belongs to
+         * <P>Type: INTEGER (if set, non-0 means true)</P>
+         */
+        public static final String IS_PRIMARY = "is_primary";
+
+        /**
+         * Whether this is the primary entry of its kind for the aggregate it belongs to. Any data
+         * record that is "super primary" must also be "primary".
+         * <P>Type: INTEGER (if set, non-0 means true)</P>
+         */
+        public static final String IS_SUPER_PRIMARY = "is_super_primary";
+
         /** Generic data column, the meaning is {@link #MIMETYPE} specific */
         public static final String DATA1 = "data1";
         /** Generic data column, the meaning is {@link #MIMETYPE} specific */
@@ -309,22 +412,16 @@
             public static final String TYPE = "data1";
 
             /**
-             * The user defined label for the the contact method.
-             * <P>Type: TEXT</P>
-             */
-            public static final String LABEL = "data2";
-
-            /**
              * The data for the contact method.
              * <P>Type: TEXT</P>
              */
-            public static final String DATA = "data3";
+            public static final String DATA = "data2";
 
             /**
-             * Whether this is the primary entry of its kind for the contact it belongs to
-             * <P>Type: INTEGER (if set, non-0 means true)</P>
+             * The user defined label for the the contact method.
+             * <P>Type: TEXT</P>
              */
-            public static final String ISPRIMARY = "data4";
+            public static final String LABEL = "data3";
         }
 
         /**
@@ -337,27 +434,28 @@
             public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/name";
 
             /**
-             * The contact's honorific prefix, e.g. "Sir"
-             */
-            public static final String PREFIX = "data1";
-
-            /**
              * The given name for the contact.
              * <P>Type: TEXT</P>
              */
-            public static final String GIVEN_NAME = "data2";
-
-            /**
-             * The contact's middle name
-             * <P>Type: TEXT</P>
-             */
-            public static final String MIDDLE_NAME = "data3";
+            public static final String GIVEN_NAME = "data1";
 
             /**
              * The family name for the contact.
              * <P>Type: TEXT</P>
              */
-            public static final String FAMILY_NAME = "data4";
+            public static final String FAMILY_NAME = "data2";
+
+            /**
+             * The contact's honorific prefix, e.g. "Sir"
+             * <P>Type: TEXT</P>
+             */
+            public static final String PREFIX = "data3";
+
+            /**
+             * The contact's middle name
+             * <P>Type: TEXT</P>
+             */
+            public static final String MIDDLE_NAME = "data4";
 
             /**
              * The contact's honorific suffix, e.g. "Jr"
@@ -412,15 +510,15 @@
             public static final int TYPE_INITIALS = 6;
 
             /**
+             * The name itself
+             */
+            public static final String NAME = "data2";
+
+            /**
              * The user provided label, only used if TYPE is {@link #TYPE_CUSTOM}.
              * <P>Type: TEXT</P>
              */
-            public static final String LABEL = "data2";
-
-            /**
-             * The name itself
-             */
-            public static final String NAME = "data3";
+            public static final String LABEL = "data3";
         }
 
         /**
@@ -445,8 +543,7 @@
              * The phone number as the user entered it.
              * <P>Type: TEXT</P>
              */
-            public static final String NUMBER = "data3";
-
+            public static final String NUMBER = "data2";
         }
 
         /**
@@ -462,7 +559,6 @@
             public static final int TYPE_HOME = 1;
             public static final int TYPE_WORK = 2;
             public static final int TYPE_OTHER = 3;
-
         }
 
         /**
@@ -500,9 +596,11 @@
              * The predefined IM protocol types. The protocol can either be non-present, one
              * of these types, or a free-form string. These cases are encoded in the PROTOCOL
              * column as:
-             *  - null
-             *  - pre:<an integer, one of the protocols below>
-             *  - custom:<a string>
+             * <ul>
+             * <li>null</li>
+             * <li>pre:&lt;an integer, one of the protocols below&gt;</li>
+             * <li>custom:&lt;a string&gt;</li>
+             * </ul>
              */
             public static final int PROTOCOL_AIM = 0;
             public static final int PROTOCOL_MSN = 1;
@@ -512,6 +610,31 @@
             public static final int PROTOCOL_GOOGLE_TALK = 5;
             public static final int PROTOCOL_ICQ = 6;
             public static final int PROTOCOL_JABBER = 7;
+
+            public static String encodePredefinedImProtocol(int protocol) {
+               return "pre:" + protocol;
+            }
+
+            public static String encodeCustomImProtocol(String protocolString) {
+               return "custom:" + protocolString;
+            }
+
+            public static Object decodeImProtocol(String encodedString) {
+               if (encodedString == null) {
+                   return null;
+               }
+
+               if (encodedString.startsWith("pre:")) {
+                   return Integer.parseInt(encodedString.substring(4));
+               }
+
+               if (encodedString.startsWith("custom:")) {
+                   return encodedString.substring(7);
+               }
+
+               throw new IllegalArgumentException(
+                       "the value is not a valid encoded protocol, " + encodedString);
+            }
         }
 
         /**
@@ -551,13 +674,6 @@
              * <P>Type: TEXT</P>
              */
             public static final String TITLE = "data4";
-
-            /**
-             * Whether this is the primary organization
-             * <P>Type: INTEGER (if set, non-0 means true)</P>
-             */
-            public static final String ISPRIMARY = "data5";
-
         }
 
         /**
@@ -594,6 +710,9 @@
             public static final String NOTE = "data1";
         }
 
+        /**
+         * Custom ringtone associated with the contact.
+         */
         public static final class CustomRingtone implements BaseCommonColumns {
             private CustomRingtone() {}
 
@@ -611,6 +730,76 @@
              */
             public static final String RINGTONE_URI = "data2";
         }
+
+        /**
+         * Group Membership.
+         */
+        public static final class GroupMembership implements BaseCommonColumns {
+            private GroupMembership() {}
+
+            /** Mime-type used when storing this in data table. */
+            public static final String CONTENT_ITEM_TYPE =
+                    "vnd.android.cursor.item/group_membership";
+
+            /**
+             * The row id of the group that this group membership refers to. Either this or the
+             * GROUP_SOURCE_ID must be set. If they are both set then they must refer to the same
+             * group.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String GROUP_ROW_ID = "data1";
+
+            /**
+             * The source id of the group that this membership refers to. Either this or the
+             * GROUP_ROW_ID must be set. If they are both set then they must refer to the same
+             * group.
+             * <P>Type: STRING</P>
+             */
+            public static final String GROUP_SOURCE_ID = "data2";
+        }
     }
 
+    /**
+     * Constants for the contact aggregation exceptions table, which contains
+     * aggregation rules overriding those used by automatic aggregation.
+     */
+    public static final class AggregationExceptions {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private AggregationExceptions() {}
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI =
+                Uri.withAppendedPath(AUTHORITY_URI, "aggregation_exceptions");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of data.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/aggregation_exception";
+
+        /**
+         * The type of exception: {@link #TYPE_NEVER_MATCH} or {@link #TYPE_ALWAYS_MATCH}.
+         *
+         * <P>Type: INTEGER</P>
+         */
+        public static final String TYPE = "type";
+
+        public static final int TYPE_NEVER_MATCH = 0;
+        public static final int TYPE_ALWAYS_MATCH = 1;
+
+        /**
+         * A reference to the {@link android.provider.ContactsContract.Contacts#_ID} of one of
+         * the contacts that the rule applies to.
+         */
+        public static final String CONTACT_ID1 = "contact_id1";
+
+        /**
+         * A reference to the {@link android.provider.ContactsContract.Contacts#_ID} of the other
+         * contact that the rule applies to.
+         */
+        public static final String CONTACT_ID2 = "contact_id2";
+    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b433859..a632bf0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1292,6 +1292,14 @@
          * boolean (1 or 0).
          */
         public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
+        
+        /**
+         * Whether live web suggestions while the user types into search dialogs are
+         * enabled. Browsers and other search UIs should respect this, as it allows
+         * a user to avoid sending partial queries to a search engine, if it poses
+         * any privacy concern. The value is boolean (1 or 0).
+         */
+        public static final String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
 
         // Settings moved to Settings.Secure
 
@@ -2567,6 +2575,32 @@
                 "gtalk_nosync_heartbeat_ping_interval_ms";
 
         /**
+         * The maximum heartbeat interval used while on the WIFI network. 
+         */
+        public static final String GTALK_SERVICE_WIFI_MAX_HEARTBEAT_INTERVAL_MS =
+                "gtalk_wifi_max_heartbeat_ping_interval_ms";
+
+        /**
+         * The minimum interval for how frequently we send heartbeat pings to the GTalk server.
+         */
+        public static final String GTALK_SERVICE_MIN_HEARTBEAT_INTERVAL_MS =
+                "gtalk_min_heartbeat_ping_interval_ms";
+
+        /**
+         * The scale down factor used by adaptive heartbeat logic (to scale down the heartbeat
+         * interval) when the previous interval fails to get a response from the server.
+         */
+        public static final String GTALK_SERVICE_ADAPTIVE_HEARTBEAT_SCALER =
+                "gtalk_adaptive_heartbeat_scaler";
+
+        /**
+         * The trigger for adaptively scaling down the heartbeat interval. This is the number of
+         * consecutive times we failed to get a server response for sending the heartbeat ping.
+         */
+        public static final String GTALK_SERVICE_ADAPTIVE_HEARTBEAT_TRIGGER =
+                "gtalk_adaptive_heartbeat_trigger";
+
+        /**
          * How long we wait to receive a heartbeat ping acknowledgement (or another packet)
          * from the GTalk server, before deeming the connection dead.
          */
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 4078fa6..a2c253a 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -146,7 +146,13 @@
          * <P>Type: TEXT</P>
          */
         public static final String SERVICE_CENTER = "service_center";
-    }
+
+        /**
+         * Has the message been locked?
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String LOCKED = "locked";
+}
 
     /**
      * Contains all text based SMS messages.
@@ -1008,6 +1014,12 @@
          * <P>Type: INTEGER</P>
          */
         public static final String THREAD_ID = "thread_id";
+
+        /**
+         * Has the message been locked?
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String LOCKED = "locked";
     }
 
     /**
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index 952372f..060bcea 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -23,12 +23,13 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Handler;
+import android.os.RemoteException;
 
 import java.util.List;
 
 /**
  * This is a simplified version of the Search Manager service.  It no longer handles
- * presentation (UI).  Its function is to maintain the map & list of "searchable" 
+ * presentation (UI).  Its function is to maintain the map & list of "searchable"
  * items, which provides a mapping from individual activities (where a user might have
  * invoked search) to specific searchable activities (where the search will be dispatched).
  */
@@ -43,19 +44,19 @@
     private final Handler mHandler;
     private boolean mSearchablesDirty;
     private Searchables mSearchables;
-    
+
     /**
      * Initializes the Search Manager service in the provided system context.
      * Only one instance of this object should be created!
      *
      * @param context to use for accessing DB, window manager, etc.
      */
-    public SearchManagerService(Context context)  {     
+    public SearchManagerService(Context context)  {
         mContext = context;
         mHandler = new Handler();
         mSearchablesDirty = true;
         mSearchables = new Searchables(context);
-        
+
         // Setup the infrastructure for updating and maintaining the list
         // of searchable activities.
         IntentFilter filter = new IntentFilter();
@@ -64,15 +65,15 @@
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         filter.addDataScheme("package");
         mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
-        
+
         // After startup settles down, preload the searchables list,
         // which will reduce the delay when the search UI is invoked.
         mHandler.post(mRunUpdateSearchable);
     }
-    
+
     /**
      * Listens for intent broadcasts.
-     * 
+     *
      * The primary purpose here is to refresh the "searchables" list
      * if packages are added/removed.
      */
@@ -80,7 +81,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            
+
             // First, test for intents that matter at any time
             if (action.equals(Intent.ACTION_PACKAGE_ADDED) ||
                 action.equals(Intent.ACTION_PACKAGE_REMOVED) ||
@@ -91,14 +92,14 @@
             }
         }
     };
-    
+
     /**
      * This runnable (for the main handler / UI thread) will update the searchables list.
      */
     private Runnable mRunUpdateSearchable = new Runnable() {
         public void run() {
             updateSearchablesIfDirty();
-        } 
+        }
     };
 
     /**
@@ -124,7 +125,7 @@
      *
      * @param launchActivity The activity from which we're launching this search.
      * @param globalSearch If false, this will only launch the search that has been specifically
-     * defined by the application (which is usually defined as a local search).  If no default 
+     * defined by the application (which is usually defined as a local search).  If no default
      * search is defined in the current application or activity, no search will be launched.
      * If true, this will always launch a platform-global (e.g. web-based) search instead.
      * @return Returns a SearchableInfo record describing the parameters of the search,
@@ -141,7 +142,7 @@
 
         return si;
     }
-    
+
     /**
      * Returns a list of the searchable activities that can be included in global search.
      */
@@ -150,4 +151,26 @@
         return mSearchables.getSearchablesInGlobalSearchList();
     }
 
+    /**
+     * Returns a list of the searchable activities that handle web searches.
+     */
+    public List<SearchableInfo> getSearchablesForWebSearch() {
+        updateSearchablesIfDirty();
+        return mSearchables.getSearchablesForWebSearchList();
+    }
+
+    /**
+     * Returns the default searchable activity for web searches.
+     */
+    public SearchableInfo getDefaultSearchableForWebSearch() {
+        updateSearchablesIfDirty();
+        return mSearchables.getDefaultSearchableForWebSearch();
+    }
+
+    /**
+     * Sets the default searchable activity for web searches.
+     */
+    public void setDefaultWebSearch(ComponentName component) {
+        mSearchables.setDefaultWebSearch(component);
+    }
 }
diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java
index 842fc75..c083142 100644
--- a/core/java/android/server/search/SearchableInfo.java
+++ b/core/java/android/server/search/SearchableInfo.java
@@ -40,7 +40,7 @@
 public final class SearchableInfo implements Parcelable {
 
     // general debugging support
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
     private static final String LOG_TAG = "SearchableInfo";
 
     // static strings used for XML lookups.
diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java
index 62631d6..a27667b 100644
--- a/core/java/android/server/search/Searchables.java
+++ b/core/java/android/server/search/Searchables.java
@@ -16,13 +16,18 @@
 
 package android.server.search;
 
+import com.android.internal.app.ResolverActivity;
+import com.android.internal.R;
+
 import android.app.SearchManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
 import android.os.Bundle;
 import android.util.Log;
 
@@ -31,37 +36,39 @@
 import java.util.List;
 
 /**
- * This class maintains the information about all searchable activities. 
+ * This class maintains the information about all searchable activities.
  */
 public class Searchables {
 
     private static final String LOG_TAG = "Searchables";
 
     // static strings used for XML lookups, etc.
-    // TODO how should these be documented for the developer, in a more structured way than 
+    // TODO how should these be documented for the developer, in a more structured way than
     // the current long wordy javadoc in SearchManager.java ?
     private static final String MD_LABEL_DEFAULT_SEARCHABLE = "android.app.default_searchable";
     private static final String MD_SEARCHABLE_SYSTEM_SEARCH = "*";
-    
+
     private Context mContext;
-    
+
     private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null;
     private ArrayList<SearchableInfo> mSearchablesList = null;
     private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null;
+    private ArrayList<SearchableInfo> mSearchablesForWebSearchList = null;
     private SearchableInfo mDefaultSearchable = null;
-    
+    private SearchableInfo mDefaultSearchableForWebSearch = null;
+
     /**
-     * 
+     *
      * @param context Context to use for looking up activities etc.
      */
     public Searchables (Context context) {
         mContext = context;
     }
-    
+
     /**
      * Look up, or construct, based on the activity.
-     * 
-     * The activities fall into three cases, based on meta-data found in 
+     *
+     * The activities fall into three cases, based on meta-data found in
      * the manifest entry:
      * <ol>
      * <li>The activity itself implements search.  This is indicated by the
@@ -73,16 +80,16 @@
      * case the factory will "redirect" and return the searchable data.</li>
      * <li>No searchability data is provided.  We return null here and other
      * code will insert the "default" (e.g. contacts) search.
-     * 
+     *
      * TODO: cache the result in the map, and check the map first.
      * TODO: it might make sense to implement the searchable reference as
      * an application meta-data entry.  This way we don't have to pepper each
      * and every activity.
      * TODO: can we skip the constructor step if it's a non-searchable?
-     * TODO: does it make sense to plug the default into a slot here for 
+     * TODO: does it make sense to plug the default into a slot here for
      * automatic return?  Probably not, but it's one way to do it.
      *
-     * @param activity The name of the current activity, or null if the 
+     * @param activity The name of the current activity, or null if the
      * activity does not define any explicit searchable metadata.
      */
     public SearchableInfo getSearchableInfo(ComponentName activity) {
@@ -92,18 +99,18 @@
             result = mSearchablesMap.get(activity);
             if (result != null) return result;
         }
-        
+
         // Step 2.  See if the current activity references a searchable.
         // Note:  Conceptually, this could be a while(true) loop, but there's
-        // no point in implementing reference chaining here and risking a loop.  
+        // no point in implementing reference chaining here and risking a loop.
         // References must point directly to searchable activities.
-       
+
         ActivityInfo ai = null;
         try {
             ai = mContext.getPackageManager().
                        getActivityInfo(activity, PackageManager.GET_META_DATA );
             String refActivityName = null;
-            
+
             // First look for activity-specific reference
             Bundle md = ai.metaData;
             if (md != null) {
@@ -116,11 +123,11 @@
                     refActivityName = md.getString(MD_LABEL_DEFAULT_SEARCHABLE);
                 }
             }
-            
+
             // Irrespective of source, if a reference was found, follow it.
             if (refActivityName != null)
             {
-                // An app or activity can declare that we should simply launch 
+                // An app or activity can declare that we should simply launch
                 // "system default search" if search is invoked.
                 if (refActivityName.equals(MD_SEARCHABLE_SYSTEM_SEARCH)) {
                     return getDefaultSearchable();
@@ -146,80 +153,101 @@
         } catch (PackageManager.NameNotFoundException e) {
             // case 3: no metadata
         }
- 
+
         // Step 3.  None found. Return null.
         return null;
-        
+
     }
-    
+
     /**
      * Provides the system-default search activity, which you can use
      * whenever getSearchableInfo() returns null;
-     * 
+     *
      * @return Returns the system-default search activity, null if never defined
      */
     public synchronized SearchableInfo getDefaultSearchable() {
         return mDefaultSearchable;
     }
-    
+
     public synchronized boolean isDefaultSearchable(SearchableInfo searchable) {
         return searchable == mDefaultSearchable;
     }
-   
+
     /**
-     * Builds an entire list (suitable for display) of 
-     * activities that are searchable, by iterating the entire set of 
-     * ACTION_SEARCH intents.  
-     * 
+     * Builds an entire list (suitable for display) of
+     * activities that are searchable, by iterating the entire set of
+     * ACTION_SEARCH & ACTION_WEB_SEARCH intents.
+     *
      * Also clears the hash of all activities -> searches which will
      * refill as the user clicks "search".
-     * 
+     *
      * This should only be done at startup and again if we know that the
      * list has changed.
-     * 
+     *
      * TODO: every activity that provides a ACTION_SEARCH intent should
      * also provide searchability meta-data.  There are a bunch of checks here
      * that, if data is not found, silently skip to the next activity.  This
      * won't help a developer trying to figure out why their activity isn't
      * showing up in the list, but an exception here is too rough.  I would
      * like to find a better notification mechanism.
-     * 
+     *
      * TODO: sort the list somehow?  UI choice.
      */
     public void buildSearchableList() {
         // These will become the new values at the end of the method
-        HashMap<ComponentName, SearchableInfo> newSearchablesMap 
+        HashMap<ComponentName, SearchableInfo> newSearchablesMap
                                 = new HashMap<ComponentName, SearchableInfo>();
         ArrayList<SearchableInfo> newSearchablesList
                                 = new ArrayList<SearchableInfo>();
         ArrayList<SearchableInfo> newSearchablesInGlobalSearchList
                                 = new ArrayList<SearchableInfo>();
+        ArrayList<SearchableInfo> newSearchablesForWebSearchList
+                                = new ArrayList<SearchableInfo>();
 
         final PackageManager pm = mContext.getPackageManager();
-        
-        // use intent resolver to generate list of ACTION_SEARCH receivers
-        List<ResolveInfo> infoList;
+
+        // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers.
+        List<ResolveInfo> searchList;
         final Intent intent = new Intent(Intent.ACTION_SEARCH);
-        infoList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
-        
+        searchList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
+
+        List<ResolveInfo> webSearchInfoList;
+        final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH);
+        webSearchInfoList = pm.queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA);
+
         // analyze each one, generate a Searchables record, and record
-        if (infoList != null) {
-            int count = infoList.size();
+        if (searchList != null || webSearchInfoList != null) {
+            int search_count = (searchList == null ? 0 : searchList.size());
+            int web_search_count = (webSearchInfoList == null ? 0 : webSearchInfoList.size());
+            int count = search_count + web_search_count;
             for (int ii = 0; ii < count; ii++) {
                 // for each component, try to find metadata
-                ResolveInfo info = infoList.get(ii);
+                ResolveInfo info = (ii < search_count)
+                        ? searchList.get(ii)
+                        : webSearchInfoList.get(ii - search_count);
                 ActivityInfo ai = info.activityInfo;
-                SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai);
-                if (searchable != null) {
-                    newSearchablesList.add(searchable);
-                    newSearchablesMap.put(searchable.getSearchActivity(), searchable);
-                    if (searchable.shouldIncludeInGlobalSearch()) {
-                        newSearchablesInGlobalSearchList.add(searchable);
+                // Check first to avoid duplicate entries.
+                if (newSearchablesMap.get(new ComponentName(ai.packageName, ai.name)) == null) {
+                    SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai);
+                    if (searchable != null) {
+                        newSearchablesList.add(searchable);
+                        newSearchablesMap.put(searchable.getSearchActivity(), searchable);
+                        if (searchable.shouldIncludeInGlobalSearch()) {
+                            newSearchablesInGlobalSearchList.add(searchable);
+                        }
                     }
                 }
             }
         }
-        
+
+        if (webSearchInfoList != null) {
+            for (int i = 0; i < webSearchInfoList.size(); ++i) {
+                ActivityInfo ai = webSearchInfoList.get(i).activityInfo;
+                ComponentName component = new ComponentName(ai.packageName, ai.name);
+                newSearchablesForWebSearchList.add(newSearchablesMap.get(component));
+            }
+        }
+
         // Find the global search provider
         Intent globalSearchIntent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
         ComponentName globalSearchActivity = globalSearchIntent.resolveActivity(pm);
@@ -230,15 +258,95 @@
                     + globalSearchActivity);
         }
 
+        // Find the default web search provider.
+        ComponentName webSearchActivity = getPreferredWebSearchActivity();
+        SearchableInfo newDefaultSearchableForWebSearch = null;
+        if (webSearchActivity != null) {
+            newDefaultSearchableForWebSearch = newSearchablesMap.get(webSearchActivity);
+        }
+        if (newDefaultSearchableForWebSearch == null) {
+            Log.w(LOG_TAG, "No searchable info found for new default web search activity "
+                    + webSearchActivity);
+        }
+
         // Store a consistent set of new values
         synchronized (this) {
             mSearchablesMap = newSearchablesMap;
             mSearchablesList = newSearchablesList;
             mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList;
+            mSearchablesForWebSearchList = newSearchablesForWebSearchList;
             mDefaultSearchable = newDefaultSearchable;
+            mDefaultSearchableForWebSearch = newDefaultSearchableForWebSearch;
         }
+
+        // Inform all listeners that the list of searchables has been updated.
+        mContext.sendBroadcast(new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED));
     }
-    
+
+    /**
+     * Checks if the given activity component is present in the system and if so makes it the
+     * preferred activity for handling ACTION_WEB_SEARCH.
+     * @param component Name of the component to check and set as preferred.
+     * @param action Intent action for which this activity is to be set as preferred.
+     * @return true if component was detected and set as preferred activity, false if not.
+     */
+    private boolean setPreferredActivity(ComponentName component, String action) {
+        Log.d(LOG_TAG, "Checking component " + component);
+        PackageManager pm = mContext.getPackageManager();
+        ActivityInfo ai;
+        try {
+            ai = pm.getActivityInfo(component, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+
+        // The code here to find the value for bestMatch is heavily inspired by the code
+        // in ResolverActivity where the preferred activity is set.
+        Intent intent = new Intent(action);
+        intent.addCategory(Intent.CATEGORY_DEFAULT);
+        List<ResolveInfo> webSearchActivities = pm.queryIntentActivities(intent, 0);
+        ComponentName set[] = new ComponentName[webSearchActivities.size()];
+        int bestMatch = 0;
+        for (int i = 0; i < webSearchActivities.size(); ++i) {
+            ResolveInfo ri = webSearchActivities.get(i);
+            set[i] = new ComponentName(ri.activityInfo.packageName,
+                                       ri.activityInfo.name);
+            if (ri.match > bestMatch) bestMatch = ri.match;
+        }
+
+        Log.d(LOG_TAG, "Setting preferred web search activity to " + component);
+        IntentFilter filter = new IntentFilter(action);
+        filter.addCategory(Intent.CATEGORY_DEFAULT);
+        pm.replacePreferredActivity(filter, bestMatch, set, component);
+        return true;
+    }
+
+    public ComponentName getPreferredWebSearchActivity() {
+        // Check if we have a preferred web search activity.
+        Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
+        PackageManager pm = mContext.getPackageManager();
+        ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+
+        if (ri == null || ri.activityInfo.name.equals(ResolverActivity.class.getName())) {
+            Log.d(LOG_TAG, "No preferred activity set for action web search.");
+
+            // The components in the providers array are checked in the order of declaration so the
+            // first one has the highest priority. If the component exists in the system it is set
+            // as the preferred activity to handle intent action web search.
+            String[] preferredActivities = mContext.getResources().getStringArray(
+                    com.android.internal.R.array.default_web_search_providers);
+            for (String componentName : preferredActivities) {
+                ComponentName component = ComponentName.unflattenFromString(componentName);
+                if (setPreferredActivity(component, Intent.ACTION_WEB_SEARCH)) {
+                    return component;
+                }
+            }
+        }
+
+        if (ri == null) return null;
+        return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
+    }
+
     /**
      * Returns the list of searchable activities.
      */
@@ -246,11 +354,33 @@
         ArrayList<SearchableInfo> result = new ArrayList<SearchableInfo>(mSearchablesList);
         return result;
     }
-    
+
     /**
      * Returns a list of the searchable activities that can be included in global search.
      */
     public synchronized ArrayList<SearchableInfo> getSearchablesInGlobalSearchList() {
         return new ArrayList<SearchableInfo>(mSearchablesInGlobalSearchList);
     }
+
+    /**
+     * Returns a list of the searchable activities that handle web searches.
+     */
+    public synchronized ArrayList<SearchableInfo> getSearchablesForWebSearchList() {
+        return new ArrayList<SearchableInfo>(mSearchablesForWebSearchList);
+    }
+
+    /**
+     * Returns the default searchable activity for web searches.
+     */
+    public synchronized SearchableInfo getDefaultSearchableForWebSearch() {
+        return mDefaultSearchableForWebSearch;
+    }
+
+    /**
+     * Sets the default searchable activity for web searches.
+     */
+    public synchronized void setDefaultWebSearch(ComponentName component) {
+        setPreferredActivity(component, Intent.ACTION_WEB_SEARCH);
+        buildSearchableList();
+    }
 }
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index e4dd020..987be2b 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -16,6 +16,8 @@
 
 package android.util;
 
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
 import android.os.*;
 
 
@@ -101,17 +103,46 @@
     }
 
     /**
-     * Set the display metrics' density and update parameters depend on it.
-     * @hide
+     * Update the display metrics based on the compatibility info and configuration.
+     * {@hide}
      */
-    public void updateDensity(float newDensity) {
-        float ratio = newDensity / density;
-        density = newDensity;
-        scaledDensity = density;
-        widthPixels *= ratio;
-        heightPixels *= ratio;
-        xdpi *= ratio;
-        ydpi *= ratio;
+    public void updateMetrics(CompatibilityInfo compatibilityInfo, Configuration configuration) {
+        if (compatibilityInfo.mScalingRequired) {
+            float invertedRatio = compatibilityInfo.mApplicationInvertedScale;
+            density *= invertedRatio;
+            scaledDensity *= invertedRatio;
+            xdpi *= invertedRatio;
+            ydpi *= invertedRatio;
+            widthPixels *= invertedRatio;
+            heightPixels *= invertedRatio;
+        }
+        if (!compatibilityInfo.mExpandable) {
+            // Note: this assume that configuration is updated before calling
+            // updateMetrics method.
+            int defaultWidth;
+            int defaultHeight;
+            switch (configuration.orientation) {
+                case Configuration.ORIENTATION_LANDSCAPE: {
+                    defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density);
+                    defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density);
+                    break;
+                }
+                case Configuration.ORIENTATION_UNDEFINED:
+                case Configuration.ORIENTATION_PORTRAIT:
+                case Configuration.ORIENTATION_SQUARE:
+                default: {
+                    defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density);
+                    defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density);
+                }
+            }
+            // adjust the size only when the device's screen is bigger.
+            if (defaultWidth < widthPixels) {
+                widthPixels = defaultWidth;
+            }
+            if (defaultHeight < heightPixels) {
+                heightPixels = defaultHeight;
+            }
+        }
     }
 
     public String toString() {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index e70c94c..6519852 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.content.Context;
+import android.content.res.CompatibilityInfo;
 import android.graphics.Canvas;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
@@ -138,28 +139,24 @@
     int mFormat = -1;
     int mType = -1;
     final Rect mSurfaceFrame = new Rect();
-    private final float mAppScale;
-    private final float mAppScaleInverted;
+    private final CompatibilityInfo mCompatibilityInfo;
 
     public SurfaceView(Context context) {
         super(context);
         setWillNotDraw(true);
-        mAppScale = context.getApplicationScale();
-        mAppScaleInverted = 1.0f / mAppScale;
+        mCompatibilityInfo = context.getResources().getCompatibilityInfo();
     }
     
     public SurfaceView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setWillNotDraw(true);
-        mAppScale = context.getApplicationScale();
-        mAppScaleInverted = 1.0f / mAppScale;
+        mCompatibilityInfo = context.getResources().getCompatibilityInfo();
     }
 
     public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         setWillNotDraw(true);
-        mAppScale = context.getApplicationScale();
-        mAppScaleInverted = 1.0f / mAppScale;
+        mCompatibilityInfo = context.getResources().getCompatibilityInfo();
     }
     
     /**
@@ -262,9 +259,9 @@
     public boolean dispatchTouchEvent(MotionEvent event) {
         // SurfaceView uses pre-scaled size unless fixed size is requested. This hook
         // scales the event back to the pre-scaled coordinates for such surface.
-        if (mRequestedWidth < 0 && mAppScale != 1.0f) {
+        if (mRequestedWidth < 0 && mCompatibilityInfo.mScalingRequired) {
             MotionEvent scaledBack = MotionEvent.obtain(event);
-            scaledBack.scale(mAppScale);
+            scaledBack.scale(mCompatibilityInfo.mApplicationScale);
             try {
                 return super.dispatchTouchEvent(scaledBack);
             } finally {
@@ -301,6 +298,7 @@
         if (!mHaveFrame) {
             return;
         }
+        float appScale = mCompatibilityInfo.mApplicationScale;
         
         int myWidth = mRequestedWidth;
         if (myWidth <= 0) myWidth = getWidth();
@@ -308,9 +306,9 @@
         if (myHeight <= 0) myHeight = getHeight();
 
         // Use original size for surface unless fixed size is requested.
-        if (mRequestedWidth <= 0) {
-            myWidth *= mAppScale;
-            myHeight *= mAppScale;
+        if (mRequestedWidth <= 0 && mCompatibilityInfo.mScalingRequired) {
+            myWidth *= appScale;
+            myHeight *= appScale;
         }
 
         getLocationInWindow(mLocation);
@@ -338,11 +336,11 @@
                 mFormat = mRequestedFormat;
                 mType = mRequestedType;
 
-                // Scaling window's layout here beause mLayout is not used elsewhere.
-                mLayout.x = (int) (mLeft * mAppScale);
-                mLayout.y = (int) (mTop * mAppScale);
-                mLayout.width = (int) (getWidth() * mAppScale);
-                mLayout.height = (int) (getHeight() * mAppScale);
+                // Scaling window's layout here because mLayout is not used elsewhere.
+                mLayout.x = (int) (mLeft * appScale);
+                mLayout.y = (int) (mTop * appScale);
+                mLayout.width = (int) (getWidth() * appScale);
+                mLayout.height = (int) (getHeight() * appScale);
                 mLayout.format = mRequestedFormat;
                 mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                               | WindowManager.LayoutParams.FLAG_SCALED
@@ -368,14 +366,18 @@
                 
                 mSurfaceLock.lock();
                 mDrawingStopped = !visible;
+
                 final int relayoutResult = mSession.relayout(
                     mWindow, mLayout, mWidth, mHeight,
                         visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
                         mVisibleInsets, mSurface);
 
-                mContentInsets.scale(mAppScaleInverted);
-                mVisibleInsets.scale(mAppScaleInverted);
-                mWinFrame.scale(mAppScaleInverted);
+                if (mCompatibilityInfo.mScalingRequired) {
+                    float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
+                    mContentInsets.scale(invertedScale);
+                    mVisibleInsets.scale(invertedScale);
+                    mWinFrame.scale(invertedScale);
+                }
 
                 if (localLOGV) Log.i(TAG, "New surface: " + mSurface
                         + ", vis=" + visible + ", frame=" + mWinFrame);
@@ -445,23 +447,23 @@
 
     private static class MyWindow extends IWindow.Stub {
         private final WeakReference<SurfaceView> mSurfaceView;
-        private final float mAppScale;
-        private final float mAppScaleInverted;
+        private final CompatibilityInfo mCompatibilityInfo;
 
         public MyWindow(SurfaceView surfaceView) {
             mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
-            mAppScale = surfaceView.getContext().getApplicationScale();
-            mAppScaleInverted = 1.0f / mAppScale;
+            mCompatibilityInfo = surfaceView.getContext().getResources().getCompatibilityInfo();
         }
 
         public void resized(int w, int h, Rect coveredInsets,
                 Rect visibleInsets, boolean reportDraw) {
             SurfaceView surfaceView = mSurfaceView.get();
-            float scale = mAppScaleInverted;
-            w *= scale;
-            h *= scale;
-            coveredInsets.scale(scale);
-            visibleInsets.scale(scale);
+            if (mCompatibilityInfo.mScalingRequired) {
+                float scale = mCompatibilityInfo.mApplicationInvertedScale;
+                w *= scale;
+                h *= scale;
+                coveredInsets.scale(scale);
+                visibleInsets.scale(scale);
+            }
 
             if (surfaceView != null) {
                 if (localLOGV) Log.v(
@@ -625,7 +627,9 @@
             Canvas c = null;
             if (!mDrawingStopped && mWindow != null) {
                 Rect frame = dirty != null ? dirty : mSurfaceFrame;
-                frame.scale(mAppScale);
+                if (mCompatibilityInfo.mScalingRequired) {
+                    frame.scale(mCompatibilityInfo.mApplicationScale);
+                }
                 try {
                     c = mSurface.lockCanvas(frame);
                 } catch (Exception e) {
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index aaaadef..46aea02 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -87,17 +87,17 @@
      * check that this value is set to true as not to affect performance.
      */
     public static final boolean TRACE_RECYCLER = false;
-    
+
     /**
      * The system property of dynamic switch for capturing view information
      * when it is set, we dump interested fields and methods for the view on focus
-     */    
+     */
     static final String SYSTEM_PROPERTY_CAPTURE_VIEW = "debug.captureview";
-        
+
     /**
      * The system property of dynamic switch for capturing event information
      * when it is set, we log key events, touch/motion and trackball events
-     */    
+     */
     static final String SYSTEM_PROPERTY_CAPTURE_EVENT = "debug.captureevent";
 
     /**
@@ -216,7 +216,7 @@
          * <pre>
          *
          * A specified String is output when the following is true:
-         * 
+         *
          * @return An array of int to String mappings
          */
         FlagToString[] flagMapping() default { };
@@ -228,7 +228,7 @@
          *
          * @return true if the properties of this property should be dumped
          *
-         * @see #prefix() 
+         * @see #prefix()
          */
         boolean deepExport() default false;
 
@@ -313,15 +313,15 @@
     @Retention(RetentionPolicy.RUNTIME)
     public @interface CapturedViewProperty {
         /**
-         * When retrieveReturn is true, we need to retrieve second level methods 
+         * When retrieveReturn is true, we need to retrieve second level methods
          * e.g., we need myView.getFirstLevelMethod().getSecondLevelMethod()
-         * we will set retrieveReturn = true on the annotation of 
+         * we will set retrieveReturn = true on the annotation of
          * myView.getFirstLevelMethod()
-         * @return true if we need the second level methods 
+         * @return true if we need the second level methods
          */
-        boolean retrieveReturn() default false;        
+        boolean retrieveReturn() default false;
     }
-        
+
     private static HashMap<Class<?>, Method[]> mCapturedViewMethodsForClasses = null;
     private static HashMap<Class<?>, Field[]> mCapturedViewFieldsForClasses = null;
 
@@ -401,7 +401,7 @@
      */
     public static long getViewRootInstanceCount() {
         return ViewRoot.getInstanceCount();
-    }    
+    }
 
     /**
      * Outputs a trace to the currently opened recycler traces. The trace records the type of
@@ -624,7 +624,7 @@
      *
      * This method will return immediately if TRACE_HIERARCHY is false.
      *
-     * @see #startHierarchyTracing(String, View) 
+     * @see #startHierarchyTracing(String, View)
      * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
      */
     public static void stopHierarchyTracing() {
@@ -671,7 +671,7 @@
 
         sHierarhcyRoot = null;
     }
-    
+
     static void dispatchCommand(View view, String command, String parameters,
             OutputStream clientStream) throws IOException {
 
@@ -1039,10 +1039,10 @@
 
         final ArrayList<Method> foundMethods = new ArrayList<Method>();
         methods = klass.getDeclaredMethods();
-        
+
         int count = methods.length;
         for (int i = 0; i < count; i++) {
-            final Method method = methods[i];            
+            final Method method = methods[i];
             if (method.getParameterTypes().length == 0 &&
                     method.isAnnotationPresent(ExportedProperty.class) &&
                     method.getReturnType() != Void.class) {
@@ -1075,7 +1075,7 @@
             klass = klass.getSuperclass();
         } while (klass != Object.class);
     }
-    
+
     private static void exportMethods(Context context, Object view, BufferedWriter out,
             Class<?> klass, String prefix) throws IOException {
 
@@ -1260,7 +1260,7 @@
 
         for (int j = 0; j < valuesCount; j++) {
             String name;
-            String value;
+            String value = null;
 
             final int intValue = array[j];
 
@@ -1276,7 +1276,6 @@
                 }
             }
 
-            value = String.valueOf(intValue);
             if (hasMapping) {
                 int mappingCount = mapping.length;
                 for (int k = 0; k < mappingCount; k++) {
@@ -1289,7 +1288,9 @@
             }
 
             if (resolveId) {
-                value = (String) resolveId(context, intValue);
+                if (value == null) value = (String) resolveId(context, intValue);
+            } else {
+                value = String.valueOf(intValue);
             }
 
             writeEntry(out, prefix, name, suffix, value);
@@ -1397,10 +1398,10 @@
 
         final ArrayList<Method> foundMethods = new ArrayList<Method>();
         methods = klass.getMethods();
-        
+
         int count = methods.length;
         for (int i = 0; i < count; i++) {
-            final Method method = methods[i];            
+            final Method method = methods[i];
             if (method.getParameterTypes().length == 0 &&
                     method.isAnnotationPresent(CapturedViewProperty.class) &&
                     method.getReturnType() != Void.class) {
@@ -1414,14 +1415,14 @@
 
         return methods;
     }
-              
-    private static String capturedViewExportMethods(Object obj, Class<?> klass, 
+
+    private static String capturedViewExportMethods(Object obj, Class<?> klass,
             String prefix) {
 
         if (obj == null) {
             return "null";
         }
-        
+
         StringBuilder sb = new StringBuilder();
         final Method[] methods = capturedViewGetPropertyMethods(klass);
 
@@ -1431,41 +1432,41 @@
             try {
                 Object methodValue = method.invoke(obj, (Object[]) null);
                 final Class<?> returnType = method.getReturnType();
-                
+
                 CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class);
                 if (property.retrieveReturn()) {
                     //we are interested in the second level data only
                     sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#"));
-                } else {                    
+                } else {
                     sb.append(prefix);
                     sb.append(method.getName());
                     sb.append("()=");
-                    
+
                     if (methodValue != null) {
-                        final String value = methodValue.toString().replace("\n", "\\n");                        
-                        sb.append(value);                        
+                        final String value = methodValue.toString().replace("\n", "\\n");
+                        sb.append(value);
                     } else {
                         sb.append("null");
                     }
                     sb.append("; ");
                 }
               } catch (IllegalAccessException e) {
-                  //Exception IllegalAccess, it is OK here 
+                  //Exception IllegalAccess, it is OK here
                   //we simply ignore this method
               } catch (InvocationTargetException e) {
-                  //Exception InvocationTarget, it is OK here 
+                  //Exception InvocationTarget, it is OK here
                   //we simply ignore this method
-              }              
-        }        
+              }
+        }
         return sb.toString();
     }
 
     private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) {
-        
+
         if (obj == null) {
             return "null";
         }
-        
+
         StringBuilder sb = new StringBuilder();
         final Field[] fields = capturedViewGetPropertyFields(klass);
 
@@ -1487,25 +1488,25 @@
                 }
                 sb.append(' ');
             } catch (IllegalAccessException e) {
-                //Exception IllegalAccess, it is OK here 
+                //Exception IllegalAccess, it is OK here
                 //we simply ignore this field
             }
         }
         return sb.toString();
     }
-    
+
     /**
-     * Dump view info for id based instrument test generation 
+     * Dump view info for id based instrument test generation
      * (and possibly further data analysis). The results are dumped
-     * to the log. 
+     * to the log.
      * @param tag for log
      * @param view for dump
      */
-    public static void dumpCapturedView(String tag, Object view) {        
+    public static void dumpCapturedView(String tag, Object view) {
         Class<?> klass = view.getClass();
         StringBuilder sb = new StringBuilder(klass.getName() + ": ");
         sb.append(capturedViewExportFields(view, klass, ""));
-        sb.append(capturedViewExportMethods(view, klass, ""));        
-        Log.d(tag, sb.toString());        
+        sb.append(capturedViewExportMethods(view, klass, ""));
+        Log.d(tag, sb.toString());
     }
 }
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index d999119..e15a61c 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -30,6 +30,7 @@
 import android.os.SystemProperties;
 import android.util.AndroidRuntimeException;
 import android.util.Config;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.EventLog;
 import android.util.SparseArray;
@@ -40,6 +41,7 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Scroller;
 import android.content.pm.PackageManager;
+import android.content.res.CompatibilityInfo;
 import android.content.Context;
 import android.app.ActivityManagerNative;
 import android.Manifest;
@@ -128,9 +130,8 @@
     int mHeight;
     Rect mDirty; // will be a graphics.Region soon
     boolean mIsAnimating;
-    // TODO: change these to scalar class.
-    private float mAppScale;
-    private float mAppScaleInverted; // = 1.0f / mAppScale
+    
+    private CompatibilityInfo mCompatibilityInfo;
     private int[] mWindowLayoutParamsBackup = null;
 
     final View.AttachInfo mAttachInfo;
@@ -393,12 +394,15 @@
         synchronized (this) {
             if (mView == null) {
                 mView = view;
-                mAppScale = mView.getContext().getApplicationScale();
-                if (mAppScale != 1.0f) {
+                mWindowAttributes.copyFrom(attrs);
+                mCompatibilityInfo =
+                        mView.getContext().getResources().getCompatibilityInfo();
+                if (mCompatibilityInfo.mScalingRequired) {
                     mWindowLayoutParamsBackup = new int[4];
                 }
-                mAppScaleInverted = 1.0f / mAppScale;
-                mWindowAttributes.copyFrom(attrs);
+                if (!mCompatibilityInfo.mExpandable) {
+                    adjustWindowAttributesForCompatibleMode(mWindowAttributes);
+                }
                 mSoftInputMode = attrs.softInputMode;
                 mWindowAttributesChanged = true;
                 mAttachInfo.mRootView = view;
@@ -413,9 +417,8 @@
                 // manager, to make sure we do the relayout before receiving
                 // any other events from the system.
                 requestLayout();
-
                 try {
-                    res = sWindowSession.add(mWindow, attrs,
+                    res = sWindowSession.add(mWindow, mWindowAttributes,
                             getHostVisibility(), mAttachInfo.mContentInsets);
                 } catch (RemoteException e) {
                     mAdded = false;
@@ -424,7 +427,10 @@
                     unscheduleTraversals();
                     throw new RuntimeException("Adding window failed", e);
                 }
-                mAttachInfo.mContentInsets.scale(mAppScaleInverted);
+                if (mCompatibilityInfo.mScalingRequired) {
+                    mAttachInfo.mContentInsets.scale(
+                            mCompatibilityInfo.mApplicationInvertedScale);
+                }
                 mPendingContentInsets.set(mAttachInfo.mContentInsets);
                 mPendingVisibleInsets.set(0, 0, 0, 0);
                 if (Config.LOGV) Log.v("ViewRoot", "Added window " + mWindow);
@@ -536,13 +542,13 @@
     public void invalidateChild(View child, Rect dirty) {
         checkThread();
         if (LOCAL_LOGV) Log.v(TAG, "Invalidate child: " + dirty);
-        if (mCurScrollY != 0 || mAppScale != 1.0f) {
+        if (mCurScrollY != 0 || mCompatibilityInfo.mScalingRequired) {
             mTempRect.set(dirty);
             if (mCurScrollY != 0) {
                mTempRect.offset(0, -mCurScrollY);
             }
-            if (mAppScale != 1.0f) {
-                mTempRect.scale(mAppScale);
+            if (mCompatibilityInfo.mScalingRequired) {
+                mTempRect.scale(mCompatibilityInfo.mApplicationScale);
             }
             dirty = mTempRect;
         }
@@ -622,6 +628,8 @@
         boolean viewVisibilityChanged = mViewVisibility != viewVisibility
                 || mNewSurfaceNeeded;
 
+        float appScale = mCompatibilityInfo.mApplicationScale;
+
         WindowManager.LayoutParams params = null;
         if (mWindowAttributesChanged) {
             mWindowAttributesChanged = false;
@@ -632,9 +640,10 @@
             fullRedrawNeeded = true;
             mLayoutRequested = true;
 
-            Display d = new Display(0);
-            desiredWindowWidth = (int) (d.getWidth() * mAppScaleInverted);
-            desiredWindowHeight = (int) (d.getHeight() * mAppScaleInverted);
+            DisplayMetrics packageMetrics = 
+                mView.getContext().getResources().getDisplayMetrics();
+            desiredWindowWidth = packageMetrics.widthPixels;
+            desiredWindowHeight = packageMetrics.heightPixels;
 
             // For the very first time, tell the view hierarchy that it
             // is attached to the window.  Note that at this point the surface
@@ -703,9 +712,10 @@
                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                     windowResizesToFitContent = true;
 
-                    Display d = new Display(0);
-                    desiredWindowWidth = (int) (d.getWidth() * mAppScaleInverted);
-                    desiredWindowHeight = (int) (d.getHeight() * mAppScaleInverted);
+                    DisplayMetrics packageMetrics = 
+                        mView.getContext().getResources().getDisplayMetrics();
+                    desiredWindowWidth = packageMetrics.widthPixels;
+                    desiredWindowHeight = packageMetrics.heightPixels;
                 }
             }
 
@@ -885,7 +895,7 @@
             mHeight = frame.height();
 
             if (initialized) {
-                mGlCanvas.setViewport((int) (mWidth * mAppScale), (int) (mHeight * mAppScale));
+                mGlCanvas.setViewport((int) (mWidth * appScale), (int) (mHeight * appScale));
             }
 
             boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
@@ -975,11 +985,7 @@
                         mTmpLocation[1] + host.mBottom - host.mTop);
 
                 host.gatherTransparentRegion(mTransparentRegion);
-
-                // TODO: scale the region, like:
-                // Region uses native methods. We probabl should have ScalableRegion class.
-
-                // Region does not have equals method ?
+                mTransparentRegion.scale(appScale);                
                 if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
                     mPreviousTransparentRegion.set(mTransparentRegion);
                     // reconfigure window manager
@@ -990,7 +996,6 @@
                 }
             }
 
-
             if (DBG) {
                 System.out.println("======================================");
                 System.out.println("performTraversals -- after setFrame");
@@ -1010,10 +1015,11 @@
             givenContent.left = givenContent.top = givenContent.right
                     = givenContent.bottom = givenVisible.left = givenVisible.top
                     = givenVisible.right = givenVisible.bottom = 0;
-            insets.contentInsets.scale(mAppScale);
-            insets.visibleInsets.scale(mAppScale);
-
             attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
+            if (mCompatibilityInfo.mScalingRequired) {
+                insets.contentInsets.scale(appScale);
+                insets.visibleInsets.scale(appScale);
+            }
             if (insetsPending || !mLastGivenInsets.equals(insets)) {
                 mLastGivenInsets.set(insets);
                 try {
@@ -1161,6 +1167,8 @@
             mCurScrollY = yoff;
             fullRedrawNeeded = true;
         }
+        float appScale = mCompatibilityInfo.mApplicationScale;
+        boolean scalingRequired = mCompatibilityInfo.mScalingRequired;
 
         Rect dirty = mDirty;
         if (mUseGL) {
@@ -1176,12 +1184,11 @@
                     mAttachInfo.mIgnoreDirtyState = true;
                     mView.mPrivateFlags |= View.DRAWN;
 
-                    float scale = mAppScale;
                     int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
                     try {
                         canvas.translate(0, -yoff);
-                        if (scale != 1.0f) {
-                            canvas.scale(scale, scale);
+                        if (scalingRequired) {
+                            canvas.scale(appScale, appScale);
                         }
                         mView.draw(canvas);
                         if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
@@ -1213,8 +1220,8 @@
         }
 
         if (fullRedrawNeeded) {
-            mAttachInfo.mIgnoreDirtyState = true;            
-            dirty.union(0, 0, (int) (mWidth * mAppScale), (int) (mHeight * mAppScale));
+            mAttachInfo.mIgnoreDirtyState = true;
+            dirty.union(0, 0, (int) (mWidth * appScale), (int) (mHeight * appScale));
         }
 
         if (DEBUG_ORIENTATION || DEBUG_DRAW) {
@@ -1222,7 +1229,8 @@
                     + mWindowAttributes.getTitle()
                     + ": dirty={" + dirty.left + "," + dirty.top
                     + "," + dirty.right + "," + dirty.bottom + "} surface="
-                    + surface + " surface.isValid()=" + surface.isValid());
+                    + surface + " surface.isValid()=" + surface.isValid() + ", appScale:" +
+                    appScale + ", width=" + mWidth + ", height=" + mHeight);
         }
 
         Canvas canvas;
@@ -1279,18 +1287,16 @@
                 mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
                 mView.mPrivateFlags |= View.DRAWN;
 
-                float scale = mAppScale;
                 if (DEBUG_DRAW) {
                     Context cxt = mView.getContext();
                     Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
-                            ", appScale=" + mAppScale);
+                            ", metrics=" + mView.getContext().getResources().getDisplayMetrics());
                 }
-                int saveCount =  canvas.save(Canvas.MATRIX_SAVE_FLAG);
+                int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
                 try {
                     canvas.translate(0, -yoff);
-                    if (scale != 1.0f) {
-                        // re-scale this
-                        canvas.scale(scale, scale);
+                    if (scalingRequired) {
+                        canvas.scale(appScale, appScale);
                     }
                     mView.draw(canvas);
                 } finally {
@@ -1603,8 +1609,8 @@
             } else {
                 didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE;
             }
-            if (event != null) {
-                event.scale(mAppScaleInverted);
+            if (event != null && mCompatibilityInfo.mScalingRequired) {
+                event.scale(mCompatibilityInfo.mApplicationInvertedScale);
             }
 
             try {
@@ -1732,8 +1738,9 @@
                         if (mGlWanted && !mUseGL) {
                             initializeGL();
                             if (mGlCanvas != null) {
-                                mGlCanvas.setViewport((int) (mWidth * mAppScale),
-                                        (int) (mHeight * mAppScale));
+                                float appScale = mCompatibilityInfo.mApplicationScale;
+                                mGlCanvas.setViewport(
+                                        (int) (mWidth * appScale), (int) (mHeight * appScale));
                             }
                         }
                     }
@@ -1937,8 +1944,8 @@
         } else {
             didFinish = false;
         }
-        if (event != null) {
-            event.scale(mAppScaleInverted);
+        if (event != null && mCompatibilityInfo.mScalingRequired) {
+            event.scale(mCompatibilityInfo.mApplicationInvertedScale);
         }
 
         if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
@@ -2368,27 +2375,59 @@
 
     private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
             boolean insetsPending) throws RemoteException {
-
         boolean restore = false;
-        if (params != null && mAppScale != 1.0f) {
+        float appScale = mCompatibilityInfo.mApplicationScale;
+        boolean scalingRequired = mCompatibilityInfo.mScalingRequired;
+        
+        if (params != null && !mCompatibilityInfo.mExpandable) {
+            adjustWindowAttributesForCompatibleMode(params);
+        }
+        if (params != null && scalingRequired) {
             restore = true;
-            params.scale(mAppScale, mWindowLayoutParamsBackup);
+            params.scale(appScale, mWindowLayoutParamsBackup);
         }
         int relayoutResult = sWindowSession.relayout(
                 mWindow, params,
-                (int) (mView.mMeasuredWidth * mAppScale),
-                (int) (mView.mMeasuredHeight * mAppScale),
+                (int) (mView.mMeasuredWidth * appScale),
+                (int) (mView.mMeasuredHeight * appScale),
                 viewVisibility, insetsPending, mWinFrame,
                 mPendingContentInsets, mPendingVisibleInsets, mSurface);
         if (restore) {
             params.restore(mWindowLayoutParamsBackup);
         }
-
-        mPendingContentInsets.scale(mAppScaleInverted);
-        mPendingVisibleInsets.scale(mAppScaleInverted);
-        mWinFrame.scale(mAppScaleInverted);
+        if (scalingRequired) {
+            float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
+            mPendingContentInsets.scale(invertedScale);
+            mPendingVisibleInsets.scale(invertedScale);
+            mWinFrame.scale(invertedScale);
+        }
         return relayoutResult;
     }
+    
+    /**
+     * Adjust the window's layout parameter for compatibility mode. It replaces FILL_PARENT
+     * with the default window size, and centers if the window wanted to fill
+     * horizontally.
+     * 
+     * @param attrs the window's layout params to adjust
+     */
+    private void adjustWindowAttributesForCompatibleMode(WindowManager.LayoutParams attrs) {
+        // fix app windows only
+        if (attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+            DisplayMetrics metrics = mView.getContext().getResources().getDisplayMetrics();
+            // TODO: improve gravity logic
+            if (attrs.width == ViewGroup.LayoutParams.FILL_PARENT) {
+                attrs.width = metrics.widthPixels;
+                attrs.gravity |= Gravity.CENTER_HORIZONTAL;
+            }
+            if (attrs.height == ViewGroup.LayoutParams.FILL_PARENT) {
+                attrs.height = metrics.heightPixels;
+            }
+            if (DEBUG_LAYOUT) {
+                Log.d(TAG, "Attributes fixed for compatibility : " + attrs);
+            }
+        }
+    }
 
     /**
      * {@inheritDoc}
@@ -2493,11 +2532,16 @@
                 + " visibleInsets=" + visibleInsets.toShortString()
                 + " reportDraw=" + reportDraw);
         Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED);
-
-        coveredInsets.scale(mAppScaleInverted);
-        visibleInsets.scale(mAppScaleInverted);
-        msg.arg1 = (int) (w * mAppScaleInverted);
-        msg.arg2 = (int) (h * mAppScaleInverted);
+        if (mCompatibilityInfo.mScalingRequired) {
+            float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
+            coveredInsets.scale(invertedScale);
+            visibleInsets.scale(invertedScale);
+            msg.arg1 = (int) (w * invertedScale);
+            msg.arg2 = (int) (h * invertedScale);
+        } else {
+            msg.arg1 = w;
+            msg.arg2 = h;
+        }
         msg.obj = new Rect[] { new Rect(coveredInsets), new Rect(visibleInsets) };
         sendMessage(msg);
     }
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index d7457a0..2c32d8b 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -56,11 +56,11 @@
     public static final int FEATURE_CONTEXT_MENU = 6;
     /** Flag for custom title. You cannot combine this feature with other title features. */
     public static final int FEATURE_CUSTOM_TITLE = 7;
-    /*  Flag for asking for an OpenGL enabled window.
+    /** Flag for asking for an OpenGL enabled window.
         All 2D graphics will be handled by OpenGL ES.
-        Private for now, until it is better tested (not shipping in 1.0)
+        @hide
     */
-    private static final int FEATURE_OPENGL = 8;
+    public static final int FEATURE_OPENGL = 8;
     /** Flag for setting the progress bar's visibility to VISIBLE */
     public static final int PROGRESS_VISIBILITY_ON = -1;
     /** Flag for setting the progress bar's visibility to GONE */
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 08ca209..e04ae72 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -121,7 +121,7 @@
         mDatabase = WebViewDatabase.getInstance(context);
         mWebViewCore = w;
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.BROWSER_FRAME) {
             Log.v(LOGTAG, "BrowserFrame constructor: this=" + this);
         }
     }
@@ -343,7 +343,7 @@
         switch (msg.what) {
             case FRAME_COMPLETED: {
                 if (mSettings.getSavePassword() && hasPasswordField()) {
-                    if (WebView.DEBUG) {
+                    if (DebugFlags.BROWSER_FRAME) {
                         Assert.assertNotNull(mCallbackProxy.getBackForwardList()
                                 .getCurrentItem());
                     }
@@ -492,7 +492,7 @@
             }
             if (mSettings.getSavePassword() && hasPasswordField()) {
                 try {
-                    if (WebView.DEBUG) {
+                    if (DebugFlags.BROWSER_FRAME) {
                         Assert.assertNotNull(mCallbackProxy.getBackForwardList()
                                 .getCurrentItem());
                     }
@@ -540,7 +540,7 @@
         // is this resource the main-frame top-level page?
         boolean isMainFramePage = mIsMainFrame;
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.BROWSER_FRAME) {
             Log.v(LOGTAG, "startLoadingResource: url=" + url + ", method="
                     + method + ", postData=" + postData + ", isHighPriority="
                     + isHighPriority + ", isMainFramePage=" + isMainFramePage);
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 0095b91..4d471f7f 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -320,7 +320,7 @@
             }
         }
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.CACHE_MANAGER) {
             Log.v(LOGTAG, "getCacheFile for url " + url);
         }
 
@@ -422,7 +422,7 @@
 
         mDataBase.addCache(url, cacheRet);
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.CACHE_MANAGER) {
             Log.v(LOGTAG, "saveCacheFile for url " + url);
         }
     }
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 667cb2c..c407044 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -100,6 +100,7 @@
     private static final int SWITCH_OUT_HISTORY        = 125;
     private static final int EXCEEDED_DATABASE_QUOTA   = 126;
     private static final int JS_TIMEOUT                = 127;
+    private static final int ADD_MESSAGE_TO_CONSOLE    = 128;
 
     // Message triggered by the client to resume execution
     private static final int NOTIFY                    = 200;
@@ -581,6 +582,13 @@
             case SWITCH_OUT_HISTORY:
                 mWebView.switchOutDrawHistory();
                 break;
+
+            case ADD_MESSAGE_TO_CONSOLE:
+                String message = msg.getData().getString("message");
+                String sourceID = msg.getData().getString("sourceID");
+                int lineNumber = msg.getData().getInt("lineNumber");
+                mWebChromeClient.addMessageToConsole(message, lineNumber, sourceID);
+                break;
         }
     }
 
@@ -852,7 +860,7 @@
             String password, Message resumeMsg) {
         // resumeMsg should be null at this point because we want to create it
         // within the CallbackProxy.
-        if (WebView.DEBUG) {
+        if (DebugFlags.CALLBACK_PROXY) {
             junit.framework.Assert.assertNull(resumeMsg);
         }
         resumeMsg = obtainMessage(NOTIFY);
@@ -1087,6 +1095,28 @@
     }
 
     /**
+     * Called by WebViewCore when we have a message to be added to the JavaScript
+     * error console. Sends a message to the Java side with the details.
+     * @param message The message to add to the console.
+     * @param lineNumber The lineNumber of the source file on which the error
+     *     occurred.
+     * @param sourceID The filename of the source file in which the error
+     *     occurred.
+     * @hide pending API counsel.
+     */
+    public void addMessageToConsole(String message, int lineNumber, String sourceID) {
+        if (mWebChromeClient == null) {
+            return;
+        }
+
+        Message msg = obtainMessage(ADD_MESSAGE_TO_CONSOLE);
+        msg.getData().putString("message", message);
+        msg.getData().putString("sourceID", sourceID);
+        msg.getData().putInt("lineNumber", lineNumber);
+        sendMessage(msg);
+    }
+
+    /**
      * @hide pending API council approval
      */
     public boolean onJsTimeout() {
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index e8c2279..7b91724 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -262,7 +262,7 @@
         if (!mAcceptCookie || uri == null) {
             return;
         }
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.COOKIE_MANAGER) {
             Log.v(LOGTAG, "setCookie: uri: " + uri + " value: " + value);
         }
 
@@ -427,12 +427,12 @@
             }
         }
         if (ret.length() > 0) {
-            if (WebView.LOGV_ENABLED) {
+            if (DebugFlags.COOKIE_MANAGER) {
                 Log.v(LOGTAG, "getCookie: uri: " + uri + " value: " + ret);
             }
             return ret.toString();
         } else {
-            if (WebView.LOGV_ENABLED) {
+            if (DebugFlags.COOKIE_MANAGER) {
                 Log.v(LOGTAG, "getCookie: uri: " + uri
                         + " But can't find cookie.");
             }
@@ -588,7 +588,7 @@
             Iterator<ArrayList<Cookie>> listIter = cookieLists.iterator();
             while (listIter.hasNext() && count < MAX_RAM_COOKIES_COUNT) {
                 ArrayList<Cookie> list = listIter.next();
-                if (WebView.DEBUG) {
+                if (DebugFlags.COOKIE_MANAGER) {
                     Iterator<Cookie> iter = list.iterator();
                     while (iter.hasNext() && count < MAX_RAM_COOKIES_COUNT) {
                         Cookie cookie = iter.next();
@@ -608,7 +608,7 @@
 
         ArrayList<Cookie> retlist = new ArrayList<Cookie>();
         if (mapSize >= MAX_RAM_DOMAIN_COUNT || count >= MAX_RAM_COOKIES_COUNT) {
-            if (WebView.DEBUG) {
+            if (DebugFlags.COOKIE_MANAGER) {
                 Log.v(LOGTAG, count + " cookies used " + byteCount
                         + " bytes with " + mapSize + " domains");
             }
@@ -616,7 +616,7 @@
             int toGo = mapSize / 10 + 1;
             while (toGo-- > 0){
                 String domain = domains[toGo].toString();
-                if (WebView.LOGV_ENABLED) {
+                if (DebugFlags.COOKIE_MANAGER) {
                     Log.v(LOGTAG, "delete domain: " + domain
                             + " from RAM cache");
                 }
diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java
index aa6c76b..14375d2 100644
--- a/core/java/android/webkit/CookieSyncManager.java
+++ b/core/java/android/webkit/CookieSyncManager.java
@@ -170,7 +170,7 @@
     }
 
     protected void syncFromRamToFlash() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.COOKIE_SYNC_MANAGER) {
             Log.v(LOGTAG, "CookieSyncManager::syncFromRamToFlash STARTS");
         }
 
@@ -187,7 +187,7 @@
                 CookieManager.getInstance().deleteLRUDomain();
         syncFromRamToFlash(lruList);
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.COOKIE_SYNC_MANAGER) {
             Log.v(LOGTAG, "CookieSyncManager::syncFromRamToFlash DONE");
         }
     }
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
new file mode 100644
index 0000000..89cb606
--- /dev/null
+++ b/core/java/android/webkit/DebugFlags.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+/**
+ * This class is a container for all of the debug flags used in the Java
+ * components of webkit.  These flags must be final in order to ensure that
+ * the compiler optimizes the code that uses them out of the final executable.
+ *
+ * The name of each flags maps directly to the name of the class in which that
+ * flag is used.
+ *
+ */
+class DebugFlags {
+
+    public static final boolean BROWSER_FRAME = false;
+    public static final boolean CACHE_MANAGER = false;
+    public static final boolean CALLBACK_PROXY = false;
+    public static final boolean COOKIE_MANAGER = false;
+    public static final boolean COOKIE_SYNC_MANAGER = false;
+    public static final boolean FRAME_LOADER = false;
+    public static final boolean J_WEB_CORE_JAVA_BRIDGE = false;// HIGHLY VERBOSE
+    public static final boolean LOAD_LISTENER = false;
+    public static final boolean NETWORK = false;
+    public static final boolean SSL_ERROR_HANDLER = false;
+    public static final boolean STREAM_LOADER = false;
+    public static final boolean URL_UTIL = false;
+    public static final boolean WEB_BACK_FORWARD_LIST = false;
+    public static final boolean WEB_SETTINGS = false;
+    public static final boolean WEB_SYNC_MANAGER = false;
+    public static final boolean WEB_VIEW = false;
+    public static final boolean WEB_VIEW_CORE = false;
+
+}
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 6f1b160..c33744e 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -120,7 +120,7 @@
         } else if (handleLocalFile(url, mListener, mSettings)) {
             return true;
         }
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.FRAME_LOADER) {
             Log.v(LOGTAG, "FrameLoader.executeLoad: url protocol not supported:"
                     + mListener.url());
         }
@@ -180,7 +180,7 @@
             return true;
         }
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.FRAME_LOADER) {
             Log.v(LOGTAG, "FrameLoader: http " + mMethod + " load for: "
                     + mListener.url());
         }
@@ -211,7 +211,7 @@
      * setup a load from the byte stream in a CacheResult.
      */
     private void startCacheLoad(CacheResult result) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.FRAME_LOADER) {
             Log.v(LOGTAG, "FrameLoader: loading from cache: "
                   + mListener.url());
         }
@@ -285,7 +285,7 @@
             // of it's state. If it is not in the cache, then go to the 
             // network.
             case WebSettings.LOAD_CACHE_ELSE_NETWORK: {
-                if (WebView.LOGV_ENABLED) {
+                if (DebugFlags.FRAME_LOADER) {
                     Log.v(LOGTAG, "FrameLoader: checking cache: "
                             + mListener.url());
                 }
diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java
index bf05518..878b690 100644
--- a/core/java/android/webkit/JWebCoreJavaBridge.java
+++ b/core/java/android/webkit/JWebCoreJavaBridge.java
@@ -190,7 +190,7 @@
      * @param timemillis The relative time when the timer should fire
      */
     private void setSharedTimer(long timemillis) {
-        if (WebView.LOGV_ENABLED) Log.v(LOGTAG, "setSharedTimer " + timemillis);
+        if (DebugFlags.J_WEB_CORE_JAVA_BRIDGE) Log.v(LOGTAG, "setSharedTimer " + timemillis);
 
         if (timemillis <= 0) {
             // we don't accumulate the sharedTimer unless it is a delayed
@@ -214,7 +214,7 @@
      * Stop the shared timer.
      */
     private void stopSharedTimer() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.J_WEB_CORE_JAVA_BRIDGE) {
             Log.v(LOGTAG, "stopSharedTimer removing all timers");
         }
         removeMessages(TIMER_MESSAGE);
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index d3e26bd..1ebdb79 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -131,7 +131,7 @@
 
     LoadListener(Context context, BrowserFrame frame, String url,
             int nativeLoader, boolean synchronous, boolean isMainPageLoader) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener constructor url=" + url);
         }
         mContext = context;
@@ -280,7 +280,7 @@
      * directly
      */
     public void headers(Headers headers) {
-        if (WebView.LOGV_ENABLED) Log.v(LOGTAG, "LoadListener.headers");
+        if (DebugFlags.LOAD_LISTENER) Log.v(LOGTAG, "LoadListener.headers");
         sendMessageInternal(obtainMessage(MSG_CONTENT_HEADERS, headers));
     }
 
@@ -422,7 +422,7 @@
      */
     public void status(int majorVersion, int minorVersion,
             int code, /* Status-Code value */ String reasonPhrase) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener: from: " + mUrl
                     + " major: " + majorVersion
                     + " minor: " + minorVersion
@@ -482,7 +482,7 @@
      * directly
      */
     public void error(int id, String description) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.error url:" +
                     url() + " id:" + id + " description:" + description);
         }
@@ -510,7 +510,7 @@
      * mDataBuilder is a thread-safe structure.
      */
     public void data(byte[] data, int length) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.data(): url: " + url());
         }
 
@@ -535,7 +535,7 @@
      * directly
      */
     public void endData() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.endData(): url: " + url());
         }
         sendMessageInternal(obtainMessage(MSG_CONTENT_FINISHED));
@@ -588,7 +588,7 @@
                 // before calling it.
                 if (mCacheLoader != null) {
                     mCacheLoader.load();
-                    if (WebView.LOGV_ENABLED) {
+                    if (DebugFlags.LOAD_LISTENER) {
                         Log.v(LOGTAG, "LoadListener cache load url=" + url());
                     }
                     return;
@@ -638,7 +638,7 @@
                     CacheManager.HEADER_KEY_IFNONEMATCH) &&
                     !headers.containsKey(
                             CacheManager.HEADER_KEY_IFMODIFIEDSINCE)) {
-                if (WebView.LOGV_ENABLED) {
+                if (DebugFlags.LOAD_LISTENER) {
                     Log.v(LOGTAG, "FrameLoader: HTTP URL in cache " +
                             "and usable: " + url());
                 }
@@ -657,7 +657,7 @@
      * directly
      */
     public void handleSslErrorRequest(SslError error) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG,
                     "LoadListener.handleSslErrorRequest(): url:" + url() +
                     " primary error: " + error.getPrimaryError() +
@@ -723,7 +723,7 @@
      * are null, cancel the request.
      */
     void handleAuthResponse(String username, String password) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.handleAuthResponse: url: " + mUrl
                     + " username: " + username
                     + " password: " + password);
@@ -820,7 +820,7 @@
     }
 
     void attachRequestHandle(RequestHandle requestHandle) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.attachRequestHandle(): " +
                     "requestHandle: " +  requestHandle);
         }
@@ -828,7 +828,7 @@
     }
 
     void detachRequestHandle() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.detachRequestHandle(): " +
                     "requestHandle: " + mRequestHandle);
         }
@@ -867,7 +867,7 @@
      */
     static boolean willLoadFromCache(String url) {
         boolean inCache = CacheManager.getCacheFile(url, null) != null;
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "willLoadFromCache: " + url + " in cache: " + 
                     inCache);
         }
@@ -1041,7 +1041,7 @@
      * EventHandler's method call.
      */
     public void cancel() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             if (mRequestHandle == null) {
                 Log.v(LOGTAG, "LoadListener.cancel(): no requestHandle");
             } else {
@@ -1173,7 +1173,7 @@
             tearDown();
         }
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.onRedirect(): redirect to: " +
                     redirectTo);
         }
@@ -1187,7 +1187,7 @@
             Pattern.compile("^((?:[xX]-)?[a-zA-Z\\*]+/[\\w\\+\\*-]+[\\.[\\w\\+-]+]*)$");
 
     /* package */ void parseContentTypeHeader(String contentType) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "LoadListener.parseContentTypeHeader: " +
                     "contentType: " + contentType);
         }
@@ -1377,7 +1377,7 @@
      */
     private String guessMimeTypeFromExtension() {
         // PENDING: need to normalize url
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.LOAD_LISTENER) {
             Log.v(LOGTAG, "guessMimeTypeFromExtension: mURL = " + mUrl);
         }
 
@@ -1401,7 +1401,7 @@
      * Cycle through our messages for synchronous loads.
      */
     /* package */ void loadSynchronousMessages() {
-        if (WebView.DEBUG && !mSynchronous) {
+        if (DebugFlags.LOAD_LISTENER && !mSynchronous) {
             throw new AssertionError();
         }
         // Note: this can be called twice if it is a synchronous network load,
diff --git a/core/java/android/webkit/Network.java b/core/java/android/webkit/Network.java
index c9b80ce..8c2b09b 100644
--- a/core/java/android/webkit/Network.java
+++ b/core/java/android/webkit/Network.java
@@ -132,7 +132,7 @@
      * XXX: Must be created in the same thread as WebCore!!!!!
      */
     private Network(Context context) {
-        if (WebView.DEBUG) {
+        if (DebugFlags.NETWORK) {
             Assert.assertTrue(Thread.currentThread().
                     getName().equals(WebViewCore.THREAD_NAME));
         }
@@ -232,7 +232,7 @@
      * connecting through the proxy.
      */
     public synchronized void setProxyUsername(String proxyUsername) {
-        if (WebView.DEBUG) {
+        if (DebugFlags.NETWORK) {
             Assert.assertTrue(isValidProxySet());
         }
 
@@ -252,7 +252,7 @@
      * connecting through the proxy.
      */
     public synchronized void setProxyPassword(String proxyPassword) {
-        if (WebView.DEBUG) {
+        if (DebugFlags.NETWORK) {
             Assert.assertTrue(isValidProxySet());
         }
 
@@ -266,7 +266,7 @@
      * @return True iff succeeds.
      */
     public boolean saveState(Bundle outState) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.NETWORK) {
             Log.v(LOGTAG, "Network.saveState()");
         }
 
@@ -280,7 +280,7 @@
      * @return True iff succeeds.
      */
     public boolean restoreState(Bundle inState) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.NETWORK) {
             Log.v(LOGTAG, "Network.restoreState()");
         }
 
@@ -300,7 +300,7 @@
      * @param loader The loader that resulted in SSL errors.
      */
     public void handleSslErrorRequest(LoadListener loader) {
-        if (WebView.DEBUG) Assert.assertNotNull(loader);
+        if (DebugFlags.NETWORK) Assert.assertNotNull(loader);
         if (loader != null) {
             mSslErrorHandler.handleSslErrorRequest(loader);
         }
@@ -313,7 +313,7 @@
      * authentication request.
      */
     public void handleAuthRequest(LoadListener loader) {
-        if (WebView.DEBUG) Assert.assertNotNull(loader);
+        if (DebugFlags.NETWORK) Assert.assertNotNull(loader);
         if (loader != null) {
             mHttpAuthHandler.handleAuthRequest(loader);
         }
diff --git a/core/java/android/webkit/SslErrorHandler.java b/core/java/android/webkit/SslErrorHandler.java
index 617a3145..cc1e750 100644
--- a/core/java/android/webkit/SslErrorHandler.java
+++ b/core/java/android/webkit/SslErrorHandler.java
@@ -120,7 +120,7 @@
      * Handles SSL error(s) on the way up to the user.
      */
     /* package */ synchronized void handleSslErrorRequest(LoadListener loader) {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.SSL_ERROR_HANDLER) {
             Log.v(LOGTAG, "SslErrorHandler.handleSslErrorRequest(): " +
                   "url=" + loader.url());
         }
@@ -157,14 +157,14 @@
 
             SslError error = loader.sslError();
 
-            if (WebView.DEBUG) {
+            if (DebugFlags.SSL_ERROR_HANDLER) {
                 Assert.assertNotNull(error);
             }
 
             int primary = error.getPrimaryError();
             String host = loader.host();
 
-            if (WebView.DEBUG) {
+            if (DebugFlags.SSL_ERROR_HANDLER) {
                 Assert.assertTrue(host != null && primary != 0);
             }
 
@@ -205,11 +205,11 @@
      */
     /* package */ synchronized void handleSslErrorResponse(boolean proceed) {
         LoadListener loader = mLoaderQueue.poll();
-        if (WebView.DEBUG) {
+        if (DebugFlags.SSL_ERROR_HANDLER) {
             Assert.assertNotNull(loader);
         }
 
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.SSL_ERROR_HANDLER) {
             Log.v(LOGTAG, "SslErrorHandler.handleSslErrorResponse():"
                   + " proceed: " + proceed
                   + " url:" + loader.url());
@@ -221,7 +221,7 @@
                 int primary = loader.sslError().getPrimaryError();
                 String host = loader.host();
 
-                if (WebView.DEBUG) {
+                if (DebugFlags.SSL_ERROR_HANDLER) {
                     Assert.assertTrue(host != null && primary != 0);
                 }
                 boolean hasKey = mSslPrefTable.containsKey(host);
diff --git a/core/java/android/webkit/StreamLoader.java b/core/java/android/webkit/StreamLoader.java
index 7d6d7cc..eab3350 100644
--- a/core/java/android/webkit/StreamLoader.java
+++ b/core/java/android/webkit/StreamLoader.java
@@ -113,7 +113,7 @@
      * @see android.os.Handler#handleMessage(android.os.Message)
      */
     public void handleMessage(Message msg) {
-        if (WebView.DEBUG && mHandler.isSynchronous()) {
+        if (DebugFlags.STREAM_LOADER && mHandler.isSynchronous()) {
             throw new AssertionError();
         }
         if (mHandler.cancelled()) {
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index d6ac3e9..de70fc2 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -61,7 +61,7 @@
             webAddress = new WebAddress(inUrl);
         } catch (ParseException ex) {
 
-            if (WebView.LOGV_ENABLED) {
+            if (DebugFlags.URL_UTIL) {
                 Log.v(LOGTAG, "smartUrlFilter: failed to parse url = " + inUrl);
             }
             return retVal;
diff --git a/core/java/android/webkit/WebBackForwardList.java b/core/java/android/webkit/WebBackForwardList.java
index ffd6a11..62a5531 100644
--- a/core/java/android/webkit/WebBackForwardList.java
+++ b/core/java/android/webkit/WebBackForwardList.java
@@ -137,7 +137,7 @@
         // when removing the first item, we can assert that the index is 0.
         // This lets us change the current index without having to query the
         // native BackForwardList.
-        if (WebView.DEBUG && (index != 0)) {
+        if (DebugFlags.WEB_BACK_FORWARD_LIST && (index != 0)) {
             throw new AssertionError();
         }
         final WebHistoryItem h = mArray.remove(index);
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index bd64f89..754b1d9 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -189,4 +189,15 @@
     public boolean onJsTimeout() {
         return true;
     }
+
+    /**
+     * Add a JavaScript error message to the console. Clients should override
+     * this to process the log message as they see fit.
+     * @param message The error message to report.
+     * @param lineNumber The line number of the error.
+     * @param sourceID The name of the source file that caused the error.
+     * @hide pending API council.
+     */
+    public void addMessageToConsole(String message, int lineNumber, String sourceID) {
+    }
 }
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index e735292..ea186fd 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1190,7 +1190,7 @@
     /*package*/
     synchronized void syncSettingsAndCreateHandler(BrowserFrame frame) {
         mBrowserFrame = frame;
-        if (WebView.DEBUG) {
+        if (DebugFlags.WEB_SETTINGS) {
             junit.framework.Assert.assertTrue(frame.mNativeFrame != 0);
         }
         nativeSync(frame.mNativeFrame);
diff --git a/core/java/android/webkit/WebSyncManager.java b/core/java/android/webkit/WebSyncManager.java
index ded17ed..d3ec603 100644
--- a/core/java/android/webkit/WebSyncManager.java
+++ b/core/java/android/webkit/WebSyncManager.java
@@ -47,7 +47,7 @@
         @Override
         public void handleMessage(Message msg) {
             if (msg.what == SYNC_MESSAGE) {
-                if (WebView.LOGV_ENABLED) {
+                if (DebugFlags.WEB_SYNC_MANAGER) {
                     Log.v(LOGTAG, "*** WebSyncManager sync ***");
                 }
                 syncFromRamToFlash();
@@ -94,7 +94,7 @@
      * sync() forces sync manager to sync now
      */
     public void sync() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.WEB_SYNC_MANAGER) {
             Log.v(LOGTAG, "*** WebSyncManager sync ***");
         }
         if (mHandler == null) {
@@ -109,7 +109,7 @@
      * resetSync() resets sync manager's timer
      */
     public void resetSync() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.WEB_SYNC_MANAGER) {
             Log.v(LOGTAG, "*** WebSyncManager resetSync ***");
         }
         if (mHandler == null) {
@@ -124,7 +124,7 @@
      * startSync() requests sync manager to start sync
      */
     public void startSync() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.WEB_SYNC_MANAGER) {
             Log.v(LOGTAG, "***  WebSyncManager startSync ***, Ref count:" + 
                     mStartSyncRefCount);
         }
@@ -142,7 +142,7 @@
      * the queue to break the sync loop
      */
     public void stopSync() {
-        if (WebView.LOGV_ENABLED) {
+        if (DebugFlags.WEB_SYNC_MANAGER) {
             Log.v(LOGTAG, "*** WebSyncManager stopSync ***, Ref count:" + 
                     mStartSyncRefCount);
         }
diff --git a/core/java/android/webkit/TextDialog.java b/core/java/android/webkit/WebTextView.java
similarity index 88%
rename from core/java/android/webkit/TextDialog.java
rename to core/java/android/webkit/WebTextView.java
index 6fe64ee..4a8fa3c 100644
--- a/core/java/android/webkit/TextDialog.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -39,11 +39,11 @@
 import java.util.ArrayList;
 
 /**
- * TextDialog is a specialized version of EditText used by WebView
+ * WebTextView is a specialized version of EditText used by WebView
  * to overlay html textfields (and textareas) to use our standard
  * text editing.
  */
-/* package */ class TextDialog extends AutoCompleteTextView {
+/* package */ class WebTextView extends AutoCompleteTextView {
 
     private WebView         mWebView;
     private boolean         mSingle;
@@ -71,11 +71,11 @@
     private static final InputFilter[] NO_FILTERS = new InputFilter[0];
 
     /**
-     * Create a new TextDialog.
-     * @param   context The Context for this TextDialog.
+     * Create a new WebTextView.
+     * @param   context The Context for this WebTextView.
      * @param   webView The WebView that created this.
      */
-    /* package */ TextDialog(Context context, WebView webView) {
+    /* package */ WebTextView(Context context, WebView webView) {
         super(context);
         mWebView = webView;
         mMaxLength = -1;
@@ -95,7 +95,7 @@
         Spannable text = (Spannable) getText();
         int oldLength = text.length();
         // Normally the delete key's dom events are sent via onTextChanged.
-        // However, if the length is zero, the text did not change, so we 
+        // However, if the length is zero, the text did not change, so we
         // go ahead and pass the key down immediately.
         if (KeyEvent.KEYCODE_DEL == keyCode && 0 == oldLength) {
             sendDomEvent(event);
@@ -121,6 +121,10 @@
             if (isPopupShowing()) {
                 return super.dispatchKeyEvent(event);
             }
+            if (!mWebView.nativeCursorMatchesFocus()) {
+                return down ? mWebView.onKeyDown(keyCode, event) : mWebView
+                        .onKeyUp(keyCode, event);
+            }
             // Center key should be passed to a potential onClick
             if (!down) {
                 mWebView.shortPressOnTextField();
@@ -128,6 +132,20 @@
             // Pass to super to handle longpress.
             return super.dispatchKeyEvent(event);
         }
+        boolean isArrowKey = false;
+        switch(keyCode) {
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+                if (!mWebView.nativeCursorMatchesFocus()) {
+                    return down ? mWebView.onKeyDown(keyCode, event) : mWebView
+                            .onKeyUp(keyCode, event);
+
+                }
+                isArrowKey = true;
+                break;
+        }
 
         // Ensure there is a layout so arrow keys are handled properly.
         if (getLayout() == null) {
@@ -147,7 +165,7 @@
             oldText = "";
         }
         if (super.dispatchKeyEvent(event)) {
-            // If the TextDialog handled the key it was either an alphanumeric
+            // If the WebTextView handled the key it was either an alphanumeric
             // key, a delete, or a movement within the text. All of those are
             // ok to pass to javascript.
 
@@ -157,22 +175,11 @@
             // so do not pass down to javascript, and instead
             // return true.  If it is an arrow key or a delete key, we can go
             // ahead and pass it down.
-            boolean isArrowKey;
-            switch(keyCode) {
-                case KeyEvent.KEYCODE_DPAD_LEFT:
-                case KeyEvent.KEYCODE_DPAD_RIGHT:
-                case KeyEvent.KEYCODE_DPAD_UP:
-                case KeyEvent.KEYCODE_DPAD_DOWN:
-                    isArrowKey = true;
-                    break;
-                case KeyEvent.KEYCODE_ENTER:
-                    // For multi-line text boxes, newlines will
-                    // trigger onTextChanged for key down (which will send both
-                    // key up and key down) but not key up.
-                    mGotEnterDown = true;
-                default:
-                    isArrowKey = false;
-                    break;
+            if (KeyEvent.KEYCODE_ENTER == keyCode) {
+                // For multi-line text boxes, newlines will
+                // trigger onTextChanged for key down (which will send both
+                // key up and key down) but not key up.
+                mGotEnterDown = true;
             }
             if (maxedOut && !isArrowKey && keyCode != KeyEvent.KEYCODE_DEL) {
                 if (oldEnd == oldStart) {
@@ -216,13 +223,10 @@
             return true;
         }
         // if it is a navigation key, pass it to WebView
-        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
-                || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
-                || keyCode == KeyEvent.KEYCODE_DPAD_UP
-                || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
+        if (isArrowKey) {
             // WebView check the trackballtime in onKeyDown to avoid calling
-            // native from both trackball and key handling. As this is called 
-            // from TextDialog, we always want WebView to check with native. 
+            // native from both trackball and key handling. As this is called
+            // from WebTextView, we always want WebView to check with native.
             // Reset trackballtime to ensure it.
             mWebView.resetTrackballTime();
             return down ? mWebView.onKeyDown(keyCode, event) : mWebView
@@ -232,9 +236,9 @@
     }
 
     /**
-     *  Create a fake touch up event at (x,y) with respect to this TextDialog.
+     *  Create a fake touch up event at (x,y) with respect to this WebTextView.
      *  This is used by WebView to act as though a touch event which happened
-     *  before we placed the TextDialog actually hit it, so that it can place
+     *  before we placed the WebTextView actually hit it, so that it can place
      *  the cursor accordingly.
      */
     /* package */ void fakeTouchEvent(float x, float y) {
@@ -251,10 +255,10 @@
     }
 
     /**
-     *  Determine whether this TextDialog currently represents the node
+     *  Determine whether this WebTextView currently represents the node
      *  represented by ptr.
      *  @param  ptr Pointer to a node to compare to.
-     *  @return boolean Whether this TextDialog already represents the node
+     *  @return boolean Whether this WebTextView already represents the node
      *          pointed to by ptr.
      */
     /* package */ boolean isSameTextField(int ptr) {
@@ -274,7 +278,7 @@
         String postChange = s.toString();
         // Prevent calls to setText from invoking onTextChanged (since this will
         // mean we are on a different textfield).  Also prevent the change when
-        // going from a textfield with a string of text to one with a smaller 
+        // going from a textfield with a string of text to one with a smaller
         // limit on text length from registering the onTextChanged event.
         if (mPreChange == null || mPreChange.equals(postChange) ||
                 (mMaxLength > -1 && mPreChange.length() > mMaxLength &&
@@ -282,8 +286,7 @@
             return;
         }
         mPreChange = postChange;
-        // This was simply a delete or a cut, so just delete the 
-        // selection.
+        // This was simply a delete or a cut, so just delete the selection.
         if (before > 0 && 0 == count) {
             mWebView.deleteSelection(start, start + before);
             // For this and all changes to the text, update our cache
@@ -308,7 +311,7 @@
                     start + count - charactersFromKeyEvents,
                     start + count - charactersFromKeyEvents);
         } else {
-            // This corrects the selection which may have been affected by the 
+            // This corrects the selection which may have been affected by the
             // trackball or auto-correct.
             mWebView.setSelection(start, start + before);
         }
@@ -324,7 +327,7 @@
         }
         updateCachedTextfield();
     }
-    
+
     @Override
     public boolean onTrackballEvent(MotionEvent event) {
         if (isPopupShowing()) {
@@ -333,6 +336,11 @@
         if (event.getAction() != MotionEvent.ACTION_MOVE) {
             return false;
         }
+        // If the Cursor is not on the text input, webview should handle the
+        // trackball
+        if (!mWebView.nativeCursorMatchesFocus()) {
+            return mWebView.onTrackballEvent(event);
+        }
         Spannable text = (Spannable) getText();
         MovementMethod move = getMovementMethod();
         if (move != null && getLayout() != null &&
@@ -350,7 +358,7 @@
     }
 
     /**
-     * Remove this TextDialog from its host WebView, and return
+     * Remove this WebTextView from its host WebView, and return
      * focus to the host.
      */
     /* package */ void remove() {
@@ -379,7 +387,7 @@
         }
         return false;
     }
-    
+
     /**
      *  Send the DOM events for the specified event.
      *  @param event    KeyEvent to be translated into a DOM event.
@@ -390,7 +398,7 @@
 
     /**
      *  Always use this instead of setAdapter, as this has features specific to
-     *  the TextDialog.
+     *  the WebTextView.
      */
     public void setAdapterCustom(AutoCompleteAdapter adapter) {
         if (adapter != null) {
@@ -456,16 +464,16 @@
 
     /**
      *  Set the pointer for this node so it can be determined which node this
-     *  TextDialog represents.
+     *  WebTextView represents.
      *  @param  ptr Integer representing the pointer to the node which this
-     *          TextDialog represents.
+     *          WebTextView represents.
      */
     /* package */ void setNodePointer(int ptr) {
         mNodePointer = ptr;
     }
 
     /**
-     * Determine the position and size of TextDialog, and add it to the
+     * Determine the position and size of WebTextView, and add it to the
      * WebView's view heirarchy.  All parameters are presumed to be in
      * view coordinates.  Also requests Focus and sets the cursor to not
      * request to be in view.
@@ -503,7 +511,8 @@
      * removing the password input type.
      */
     public void setSingleLine(boolean single) {
-        int inputType = EditorInfo.TYPE_CLASS_TEXT;
+        int inputType = EditorInfo.TYPE_CLASS_TEXT
+                | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
         if (!single) {
             inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
                     | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
@@ -515,8 +524,8 @@
     }
 
     /**
-     * Set the text for this TextDialog, and set the selection to (start, end)
-     * @param   text    Text to go into this TextDialog.
+     * Set the text for this WebTextView, and set the selection to (start, end)
+     * @param   text    Text to go into this WebTextView.
      * @param   start   Beginning of the selection.
      * @param   end     End of the selection.
      */
@@ -547,7 +556,7 @@
         edit.replace(0, edit.length(), text);
         updateCachedTextfield();
     }
-    
+
     /**
      *  Update the cache to reflect the current text.
      */
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index eef128b..2940983 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -58,7 +58,7 @@
 import android.view.ViewTreeObserver;
 import android.view.animation.AlphaAnimation;
 import android.view.inputmethod.InputMethodManager;
-import android.webkit.TextDialog.AutoCompleteAdapter;
+import android.webkit.WebTextView.AutoCompleteAdapter;
 import android.webkit.WebViewCore.EventHub;
 import android.widget.AbsoluteLayout;
 import android.widget.Adapter;
@@ -84,7 +84,7 @@
 import java.util.List;
 
 /**
- * <p>A View that displays web pages. This class is the basis upon which you 
+ * <p>A View that displays web pages. This class is the basis upon which you
  * can roll your own web browser or simply display some online content within your Activity.
  * It uses the WebKit rendering engine to display
  * web pages and includes methods to navigate forward and backward
@@ -93,7 +93,7 @@
  * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
  * (introduced in API version 3).
  * <p>Note that, in order for your Activity to access the Internet and load web pages
- * in a WebView, you must add the <var>INTERNET</var> permissions to your 
+ * in a WebView, you must add the <var>INTERNET</var> permissions to your
  * Android Manifest file:</p>
  * <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
  *
@@ -195,7 +195,7 @@
  * changes, and then just leave the WebView alone. It'll automatically
  * re-orient itself as appropriate.</p>
  */
-public class WebView extends AbsoluteLayout 
+public class WebView extends AbsoluteLayout
         implements ViewTreeObserver.OnGlobalFocusChangeListener,
         ViewGroup.OnHierarchyChangeListener {
 
@@ -205,10 +205,7 @@
     // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
     private boolean mAutoRedraw;
 
-    // keep debugging parameters near the top of the file
     static final String LOGTAG = "webview";
-    static final boolean DEBUG = false;
-    static final boolean LOGV_ENABLED = DEBUG;
 
     private static class ExtendedZoomControls extends FrameLayout {
         public ExtendedZoomControls(Context context, AttributeSet attrs) {
@@ -219,40 +216,40 @@
             mZoomControls = (ZoomControls) findViewById(com.android.internal.R.id.zoomControls);
             mZoomMagnify = (ImageView) findViewById(com.android.internal.R.id.zoomMagnify);
         }
-        
+
         public void show(boolean showZoom, boolean canZoomOut) {
             mZoomControls.setVisibility(showZoom ? View.VISIBLE : View.GONE);
             mZoomMagnify.setVisibility(canZoomOut ? View.VISIBLE : View.GONE);
             fade(View.VISIBLE, 0.0f, 1.0f);
         }
-        
+
         public void hide() {
             fade(View.GONE, 1.0f, 0.0f);
         }
-        
+
         private void fade(int visibility, float startAlpha, float endAlpha) {
             AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha);
             anim.setDuration(500);
             startAnimation(anim);
             setVisibility(visibility);
         }
-        
+
         public void setIsZoomMagnifyEnabled(boolean isEnabled) {
             mZoomMagnify.setEnabled(isEnabled);
         }
-        
+
         public boolean hasFocus() {
             return mZoomControls.hasFocus() || mZoomMagnify.hasFocus();
         }
-        
+
         public void setOnZoomInClickListener(OnClickListener listener) {
             mZoomControls.setOnZoomInClickListener(listener);
         }
-            
+
         public void setOnZoomOutClickListener(OnClickListener listener) {
             mZoomControls.setOnZoomOutClickListener(listener);
         }
-            
+
         public void setOnZoomMagnifyClickListener(OnClickListener listener) {
             mZoomMagnify.setOnClickListener(listener);
         }
@@ -260,7 +257,7 @@
         ZoomControls mZoomControls;
         ImageView mZoomMagnify;
     }
-    
+
     /**
      *  Transportation object for returning WebView across thread boundaries.
      */
@@ -300,7 +297,7 @@
     private WebViewCore mWebViewCore;
     // Handler for dispatching UI messages.
     /* package */ final Handler mPrivateHandler = new PrivateHandler();
-    private TextDialog mTextEntry;
+    private WebTextView mWebTextView;
     // Used to ignore changes to webkit text that arrives to the UI side after
     // more key events.
     private int mTextGeneration;
@@ -363,12 +360,12 @@
     // take control of touch events unless it says no for touch down event.
     private boolean mPreventDrag;
 
-    // If updateTextEntry gets called while we are out of focus, use this 
+    // If rebuildWebTextView gets called while we are out of focus, use this
     // variable to remember to do it next time we gain focus.
-    private boolean mNeedsUpdateTextEntry = false;
-    
-    // Whether or not to draw the focus ring.
-    private boolean mDrawFocusRing = true;
+    private boolean mNeedsRebuildWebTextView = false;
+
+    // Whether or not to draw the cursor ring.
+    private boolean mDrawCursorRing = true;
 
     // true if onPause has been called (and not onResume)
     private boolean mIsPaused;
@@ -391,7 +388,7 @@
     // needed to avoid flinging after a pause of no movement
     private static final int MIN_FLING_TIME = 250;
     // The time that the Zoom Controls are visible before fading away
-    private static final long ZOOM_CONTROLS_TIMEOUT = 
+    private static final long ZOOM_CONTROLS_TIMEOUT =
             ViewConfiguration.getZoomControlsTimeout();
     // The amount of content to overlap between two screens when going through
     // pages with the space bar, in pixels.
@@ -412,7 +409,7 @@
     private int mContentWidth;   // cache of value from WebViewCore
     private int mContentHeight;  // cache of value from WebViewCore
 
-    // Need to have the separate control for horizontal and vertical scrollbar 
+    // Need to have the separate control for horizontal and vertical scrollbar
     // style than the View's single scrollbar style
     private boolean mOverlayHorizontalScrollbar = true;
     private boolean mOverlayVerticalScrollbar = false;
@@ -427,51 +424,48 @@
 
     private boolean mWrapContent;
 
-    // true if we should call webcore to draw the content, false means we have
-    // requested something but it isn't ready to draw yet.
-    private WebViewCore.FocusData mFocusData;
     /**
      * Private message ids
      */
-    private static final int REMEMBER_PASSWORD = 1;
-    private static final int NEVER_REMEMBER_PASSWORD = 2;
-    private static final int SWITCH_TO_SHORTPRESS = 3;
-    private static final int SWITCH_TO_LONGPRESS = 4;
-    private static final int UPDATE_TEXT_ENTRY_ADAPTER = 6;
-    private static final int SWITCH_TO_ENTER = 7;
-    private static final int RESUME_WEBCORE_UPDATE = 8;
+    private static final int REMEMBER_PASSWORD          = 1;
+    private static final int NEVER_REMEMBER_PASSWORD    = 2;
+    private static final int SWITCH_TO_SHORTPRESS       = 3;
+    private static final int SWITCH_TO_LONGPRESS        = 4;
+    private static final int REQUEST_FORM_DATA          = 6;
+    private static final int SWITCH_TO_CLICK            = 7;
+    private static final int RESUME_WEBCORE_UPDATE      = 8;
 
     //! arg1=x, arg2=y
-    static final int SCROLL_TO_MSG_ID               = 10;
-    static final int SCROLL_BY_MSG_ID               = 11;
+    static final int SCROLL_TO_MSG_ID                   = 10;
+    static final int SCROLL_BY_MSG_ID                   = 11;
     //! arg1=x, arg2=y
-    static final int SPAWN_SCROLL_TO_MSG_ID         = 12;
+    static final int SPAWN_SCROLL_TO_MSG_ID             = 12;
     //! arg1=x, arg2=y
-    static final int SYNC_SCROLL_TO_MSG_ID          = 13;
-    static final int NEW_PICTURE_MSG_ID             = 14;
-    static final int UPDATE_TEXT_ENTRY_MSG_ID       = 15;
-    static final int WEBCORE_INITIALIZED_MSG_ID     = 16;
-    static final int UPDATE_TEXTFIELD_TEXT_MSG_ID   = 17;
-    static final int DID_FIRST_LAYOUT_MSG_ID        = 18;
-    static final int RECOMPUTE_FOCUS_MSG_ID         = 19;
-    static final int NOTIFY_FOCUS_SET_MSG_ID        = 20;
-    static final int MARK_NODE_INVALID_ID           = 21;
-    static final int UPDATE_CLIPBOARD               = 22;
-    static final int LONG_PRESS_ENTER               = 23;
-    static final int PREVENT_TOUCH_ID               = 24;
-    static final int WEBCORE_NEED_TOUCH_EVENTS      = 25;
+    static final int SYNC_SCROLL_TO_MSG_ID              = 13;
+    static final int NEW_PICTURE_MSG_ID                 = 14;
+    static final int UPDATE_TEXT_ENTRY_MSG_ID           = 15;
+    static final int WEBCORE_INITIALIZED_MSG_ID         = 16;
+    static final int UPDATE_TEXTFIELD_TEXT_MSG_ID       = 17;
+    static final int DID_FIRST_LAYOUT_MSG_ID            = 18;
+    static final int RECOMPUTE_FOCUS_MSG_ID             = 19;
+
+    static final int MARK_NODE_INVALID_ID               = 21;
+    static final int UPDATE_CLIPBOARD                   = 22;
+    static final int LONG_PRESS_CENTER                  = 23;
+    static final int PREVENT_TOUCH_ID                   = 24;
+    static final int WEBCORE_NEED_TOUCH_EVENTS          = 25;
     // obj=Rect in doc coordinates
-    static final int INVAL_RECT_MSG_ID              = 26;
-    
+    static final int INVAL_RECT_MSG_ID                  = 26;
+
     static final String[] HandlerDebugString = {
-        "REMEMBER_PASSWORD", // = 1;
-        "NEVER_REMEMBER_PASSWORD", // = 2;
-        "SWITCH_TO_SHORTPRESS", // = 3;
-        "SWITCH_TO_LONGPRESS", // = 4;
+        "REMEMBER_PASSWORD", //              = 1;
+        "NEVER_REMEMBER_PASSWORD", //        = 2;
+        "SWITCH_TO_SHORTPRESS", //           = 3;
+        "SWITCH_TO_LONGPRESS", //            = 4;
         "5",
-        "UPDATE_TEXT_ENTRY_ADAPTER", // = 6;
-        "SWITCH_TO_ENTER", // = 7;
-        "RESUME_WEBCORE_UPDATE", // = 8;
+        "REQUEST_FORM_DATA", //              = 6;
+        "SWITCH_TO_CLICK", //                = 7;
+        "RESUME_WEBCORE_UPDATE", //          = 8;
         "9",
         "SCROLL_TO_MSG_ID", //               = 10;
         "SCROLL_BY_MSG_ID", //               = 11;
@@ -483,10 +477,10 @@
         "UPDATE_TEXTFIELD_TEXT_MSG_ID", //   = 17;
         "DID_FIRST_LAYOUT_MSG_ID", //        = 18;
         "RECOMPUTE_FOCUS_MSG_ID", //         = 19;
-        "NOTIFY_FOCUS_SET_MSG_ID", //        = 20;
+        "20",
         "MARK_NODE_INVALID_ID", //           = 21;
         "UPDATE_CLIPBOARD", //               = 22;
-        "LONG_PRESS_ENTER", //               = 23;
+        "LONG_PRESS_CENTER", //              = 23;
         "PREVENT_TOUCH_ID", //               = 24;
         "WEBCORE_NEED_TOUCH_EVENTS", //      = 25;
         "INVAL_RECT_MSG_ID" //               = 26;
@@ -527,7 +521,7 @@
     private static final int SNAP_X_LOCK = 4;
     private static final int SNAP_Y_LOCK = 5;
     private boolean mSnapPositive;
-    
+
     // Used to match key downs and key ups
     private boolean mGotKeyDown;
 
@@ -550,7 +544,7 @@
      * URI scheme for map address
      */
     public static final String SCHEME_GEO = "geo:0,0?q=";
-    
+
     private int mBackgroundColor = Color.WHITE;
 
     // Used to notify listeners of a new picture.
@@ -638,7 +632,7 @@
     private ExtendedZoomControls mZoomControls;
     private Runnable mZoomControlRunnable;
 
-    private ZoomButtonsController mZoomButtonsController; 
+    private ZoomButtonsController mZoomButtonsController;
     private ImageView mZoomOverviewButton;
     private ImageView mZoomFitPageButton;
 
@@ -663,11 +657,11 @@
             } else {
                 zoomOut();
             }
-            
+
             updateZoomButtonsEnabled();
         }
     };
-    
+
     /**
      * Construct a new WebView with a Context object.
      * @param context A Context object used to access application assets.
@@ -698,11 +692,6 @@
         mCallbackProxy = new CallbackProxy(context, this);
         mWebViewCore = new WebViewCore(context, this, mCallbackProxy);
         mDatabase = WebViewDatabase.getInstance(context);
-        mFocusData = new WebViewCore.FocusData();
-        mFocusData.mFrame = 0;
-        mFocusData.mNode = 0;
-        mFocusData.mX = 0;
-        mFocusData.mY = 0;
         mScroller = new Scroller(context);
 
         initZoomController(context);
@@ -969,7 +958,7 @@
         clearTextEntry();
         if (mWebViewCore != null) {
             // Set the handlers to null before destroying WebViewCore so no
-            // more messages will be posted. 
+            // more messages will be posted.
             mCallbackProxy.setWebViewClient(null);
             mCallbackProxy.setWebChromeClient(null);
             // Tell WebViewCore to destroy itself
@@ -1005,7 +994,7 @@
     public static void disablePlatformNotifications() {
         Network.disablePlatformNotifications();
     }
-    
+
     /**
      * Inform WebView of the network state. This is used to set
      * the javascript property window.navigator.isOnline and
@@ -1018,7 +1007,7 @@
     }
 
     /**
-     * Save the state of this WebView used in 
+     * Save the state of this WebView used in
      * {@link android.app.Activity#onSaveInstanceState}. Please note that this
      * method no longer stores the display data for this WebView. The previous
      * behavior could potentially leak files if {@link #restoreState} was never
@@ -1149,10 +1138,10 @@
 
     /**
      * Restore the state of this WebView from the given map used in
-     * {@link android.app.Activity#onRestoreInstanceState}. This method should 
-     * be called to restore the state of the WebView before using the object. If 
-     * it is called after the WebView has had a chance to build state (load 
-     * pages, create a back/forward list, etc.) there may be undesirable 
+     * {@link android.app.Activity#onRestoreInstanceState}. This method should
+     * be called to restore the state of the WebView before using the object. If
+     * it is called after the WebView has had a chance to build state (load
+     * pages, create a back/forward list, etc.) there may be undesirable
      * side-effects. Please note that this method no longer restores the
      * display data for this WebView. See {@link #savePicture} and {@link
      * #restorePicture} for saving and restoring the display data.
@@ -1222,10 +1211,10 @@
      * Load the url with postData using "POST" method into the WebView. If url
      * is not a network url, it will be loaded with {link
      * {@link #loadUrl(String)} instead.
-     * 
+     *
      * @param url The url of the resource to load.
      * @param postData The data will be passed to "POST" request.
-     * 
+     *
      * @hide pending API solidification
      */
     public void postUrl(String url, byte[] postData) {
@@ -1267,7 +1256,7 @@
      * able to access asset files. If the baseUrl is anything other than
      * http(s)/ftp(s)/about/javascript as scheme, you can access asset files for
      * sub resources.
-     * 
+     *
      * @param baseUrl Url to resolve relative paths with, if null defaults to
      *            "about:blank"
      * @param data A String of data in the given encoding.
@@ -1278,7 +1267,7 @@
      */
     public void loadDataWithBaseURL(String baseUrl, String data,
             String mimeType, String encoding, String failUrl) {
-        
+
         if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
             loadData(data, mimeType, encoding);
             return;
@@ -1399,7 +1388,7 @@
                     ignoreSnapshot ? 1 : 0);
         }
     }
-    
+
     private boolean extendScroll(int y) {
         int finalY = mScroller.getFinalY();
         int newY = pinLocY(finalY + y);
@@ -1408,7 +1397,7 @@
         mScroller.extendDuration(computeDuration(0, y));
         return true;
     }
-    
+
     /**
      * Scroll the contents of the view up by half the view size
      * @param top true to jump to the top of the page
@@ -1432,10 +1421,10 @@
             y = -h / 2;
         }
         mUserScroll = true;
-        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0) 
+        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
                 : extendScroll(y);
     }
-    
+
     /**
      * Scroll the contents of the view down by half the page size
      * @param bottom true to jump to bottom of page
@@ -1458,7 +1447,7 @@
             y = h / 2;
         }
         mUserScroll = true;
-        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0) 
+        return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
                 : extendScroll(y);
     }
 
@@ -1471,7 +1460,7 @@
         mContentHeight = 0;
         mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
     }
-    
+
     /**
      * Return a new picture that captures the current display of the webview.
      * This is a copy of the display, and will be unaffected if the webview
@@ -1482,7 +1471,7 @@
      *         bounds of the view.
      */
     public Picture capturePicture() {
-        if (null == mWebViewCore) return null; // check for out of memory tab 
+        if (null == mWebViewCore) return null; // check for out of memory tab
         return mWebViewCore.copyContentPicture();
     }
 
@@ -1490,17 +1479,17 @@
      *  Return true if the browser is displaying a TextView for text input.
      */
     private boolean inEditingMode() {
-        return mTextEntry != null && mTextEntry.getParent() != null
-                && mTextEntry.hasFocus();
+        return mWebTextView != null && mWebTextView.getParent() != null
+                && mWebTextView.hasFocus();
     }
 
     private void clearTextEntry() {
         if (inEditingMode()) {
-            mTextEntry.remove();
+            mWebTextView.remove();
         }
     }
 
-    /** 
+    /**
      * Return the current scale of the WebView
      * @return The current scale.
      */
@@ -1564,26 +1553,26 @@
         }
 
         HitTestResult result = new HitTestResult();
-
-        if (nativeUpdateFocusNode()) {
-            FocusNode node = mFocusNode;
-            if (node.mIsTextField || node.mIsTextArea) {
+        if (nativeHasCursorNode()) {
+            if (nativeCursorIsTextInput()) {
                 result.setType(HitTestResult.EDIT_TEXT_TYPE);
-            } else if (node.mText != null) {
-                String text = node.mText;
-                if (text.startsWith(SCHEME_TEL)) {
-                    result.setType(HitTestResult.PHONE_TYPE);
-                    result.setExtra(text.substring(SCHEME_TEL.length()));
-                } else if (text.startsWith(SCHEME_MAILTO)) {
-                    result.setType(HitTestResult.EMAIL_TYPE);
-                    result.setExtra(text.substring(SCHEME_MAILTO.length()));
-                } else if (text.startsWith(SCHEME_GEO)) {
-                    result.setType(HitTestResult.GEO_TYPE);
-                    result.setExtra(URLDecoder.decode(text
-                            .substring(SCHEME_GEO.length())));
-                } else if (node.mIsAnchor) {
-                    result.setType(HitTestResult.SRC_ANCHOR_TYPE);
-                    result.setExtra(text);
+            } else {
+                String text = nativeCursorText();
+                if (text != null) {
+                    if (text.startsWith(SCHEME_TEL)) {
+                        result.setType(HitTestResult.PHONE_TYPE);
+                        result.setExtra(text.substring(SCHEME_TEL.length()));
+                    } else if (text.startsWith(SCHEME_MAILTO)) {
+                        result.setType(HitTestResult.EMAIL_TYPE);
+                        result.setExtra(text.substring(SCHEME_MAILTO.length()));
+                    } else if (text.startsWith(SCHEME_GEO)) {
+                        result.setType(HitTestResult.GEO_TYPE);
+                        result.setExtra(URLDecoder.decode(text
+                                .substring(SCHEME_GEO.length())));
+                    } else if (nativeCursorIsAnchor()) {
+                        result.setType(HitTestResult.SRC_ANCHOR_TYPE);
+                        result.setExtra(text);
+                    }
                 }
             }
         }
@@ -1595,8 +1584,8 @@
             int contentY = viewToContent((int) mLastTouchY + mScrollY);
             String text = nativeImageURI(contentX, contentY);
             if (text != null) {
-                result.setType(type == HitTestResult.UNKNOWN_TYPE ? 
-                        HitTestResult.IMAGE_TYPE : 
+                result.setType(type == HitTestResult.UNKNOWN_TYPE ?
+                        HitTestResult.IMAGE_TYPE :
                         HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
                 result.setExtra(text);
             }
@@ -1608,7 +1597,7 @@
      * Request the href of an anchor element due to getFocusNodePath returning
      * "href." If hrefMsg is null, this method returns immediately and does not
      * dispatch hrefMsg to its target.
-     * 
+     *
      * @param hrefMsg This message will be dispatched with the result of the
      *            request as the data member with "url" as key. The result can
      *            be null.
@@ -1617,22 +1606,20 @@
         if (hrefMsg == null || mNativeClass == 0) {
             return;
         }
-        if (nativeUpdateFocusNode()) {
-            FocusNode node = mFocusNode;
-            if (node.mIsAnchor) {
-                // NOTE: We may already have the url of the anchor stored in
-                // node.mText but it may be out of date or the caller may want
-                // to know about javascript urls.
-                mWebViewCore.sendMessage(EventHub.REQUEST_FOCUS_HREF,
-                        node.mFramePointer, node.mNodePointer, hrefMsg);
-            }
+        if (nativeCursorIsAnchor()) {
+            // NOTE: We may already have the url of the anchor stored in
+            // node.mText but it may be out of date or the caller may want
+            // to know about javascript urls.
+            mWebViewCore.sendMessage(EventHub.REQUEST_FOCUS_HREF,
+                    nativeCursorFramePointer(), nativeCursorNodePointer(),
+                    hrefMsg);
         }
     }
-    
+
     /**
      * Request the url of the image last touched by the user. msg will be sent
      * to its target with a String representing the url as its object.
-     * 
+     *
      * @param msg This message will be dispatched with the result of the request
      *            as the data member with "url" as key. The result can be null.
      */
@@ -1707,7 +1694,7 @@
         if ((w | h) == 0) {
             return;
         }
-        
+
         // don't abort a scroll animation if we didn't change anything
         if (mContentWidth != w || mContentHeight != h) {
             // record new dimensions
@@ -1767,7 +1754,7 @@
                 mActualScale = scale;
                 mInvActualScale = 1 / scale;
 
-                // as we don't have animation for scaling, don't do animation 
+                // as we don't have animation for scaling, don't do animation
                 // for scrolling, as it causes weird intermediate state
                 //        pinScrollTo(Math.round(sx), Math.round(sy));
                 mScrollX = pinLocX(Math.round(sx));
@@ -1891,10 +1878,10 @@
         WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
         return h != null ? h.getUrl() : null;
     }
-    
+
     /**
-     * Get the original url for the current page. This is not always the same 
-     * as the url passed to WebViewClient.onPageStarted because although the 
+     * Get the original url for the current page. This is not always the same
+     * as the url passed to WebViewClient.onPageStarted because although the
      * load for that url has begun, the current page may not have changed.
      * Also, there may have been redirects resulting in a different url to that
      * originally requested.
@@ -1932,7 +1919,7 @@
     public int getProgress() {
         return mCallbackProxy.getProgress();
     }
-    
+
     /**
      * @return the height of the HTML content.
      */
@@ -2014,7 +2001,7 @@
     public void clearFormData() {
         if (inEditingMode()) {
             AutoCompleteAdapter adapter = null;
-            mTextEntry.setAdapterCustom(adapter);
+            mWebTextView.setAdapterCustom(adapter);
         }
     }
 
@@ -2048,7 +2035,7 @@
 
     /*
      * Highlight and scroll to the next occurance of String in findAll.
-     * Wraps the page infinitely, and scrolls.  Must be called after 
+     * Wraps the page infinitely, and scrolls.  Must be called after
      * calling findAll.
      *
      * @param forward Direction to search.
@@ -2074,11 +2061,8 @@
     // or not we draw the highlights for matches.
     private boolean mFindIsUp;
 
-    private native int nativeFindAll(String findLower, String findUpper);
-    private native void nativeFindNext(boolean forward);
-    
     /**
-     * Return the first substring consisting of the address of a physical 
+     * Return the first substring consisting of the address of a physical
      * location. Currently, only addresses in the United States are detected,
      * and consist of:
      * - a house number
@@ -2091,7 +2075,7 @@
      * All names must be correctly capitalized, and the zip code, if present,
      * must be valid for the state. The street type must be a standard USPS
      * spelling or abbreviation. The state or territory must also be spelled
-     * or abbreviated using USPS standards. The house number may not exceed 
+     * or abbreviated using USPS standards. The house number may not exceed
      * five digits.
      * @param addr The string to search for addresses.
      *
@@ -2396,7 +2380,7 @@
     protected void finalize() throws Throwable {
         destroy();
     }
-    
+
     @Override
     protected void onDraw(Canvas canvas) {
         // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
@@ -2405,7 +2389,7 @@
         }
         if (mWebViewCore.mEndScaleZoom) {
             mWebViewCore.mEndScaleZoom = false;
-            if (mTouchMode >= FIRST_SCROLL_ZOOM 
+            if (mTouchMode >= FIRST_SCROLL_ZOOM
                     && mTouchMode <= LAST_SCROLL_ZOOM) {
                 setHorizontalScrollBarEnabled(true);
                 setVerticalScrollBarEnabled(true);
@@ -2427,11 +2411,11 @@
             // need to check it again.
             nativeRecordButtons(hasFocus() && hasWindowFocus(),
                     mTouchMode == TOUCH_SHORTPRESS_START_MODE
-                    || mTrackballDown || mGotEnterDown, false);
-            drawCoreAndFocusRing(canvas, mBackgroundColor, mDrawFocusRing);
+                    || mTrackballDown || mGotCenterDown, false);
+            drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
         }
         canvas.restoreToCount(sc);
-        
+
         if (AUTO_REDRAW_HACK && mAutoRedraw) {
             invalidate();
         }
@@ -2448,13 +2432,13 @@
     @Override
     public boolean performLongClick() {
         if (inEditingMode()) {
-            return mTextEntry.performLongClick();
+            return mWebTextView.performLongClick();
         } else {
             return super.performLongClick();
         }
     }
 
-    private void drawCoreAndFocusRing(Canvas canvas, int color,
+    private void drawCoreAndCursorRing(Canvas canvas, int color,
         boolean drawFocus) {
         if (mDrawHistory) {
             canvas.scale(mActualScale, mActualScale);
@@ -2463,14 +2447,14 @@
         }
 
         boolean animateZoom = mZoomScale != 0;
-        boolean animateScroll = !mScroller.isFinished() 
+        boolean animateScroll = !mScroller.isFinished()
                 || mVelocityTracker != null;
         if (animateZoom) {
             float zoomScale;
             int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
             if (interval < ZOOM_ANIMATION_LENGTH) {
                 float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
-                zoomScale = 1.0f / (mInvInitialZoomScale 
+                zoomScale = 1.0f / (mInvInitialZoomScale
                         + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
                 invalidate();
             } else {
@@ -2510,7 +2494,7 @@
             if (mTouchSelection) {
                 nativeDrawSelectionRegion(canvas);
             } else {
-                nativeDrawSelection(canvas, mSelectX, mSelectY, 
+                nativeDrawSelection(canvas, mSelectX, mSelectY,
                         mExtendSelection);
             }
         } else if (drawFocus) {
@@ -2524,7 +2508,7 @@
                             LONG_PRESS_TIMEOUT);
                 }
             }
-            nativeDrawFocusRing(canvas);
+            nativeDrawCursorRing(canvas);
         }
         // When the FindDialog is up, only draw the matches if we are not in
         // the process of scrolling them into view.
@@ -2533,14 +2517,12 @@
         }
     }
 
-    private native void nativeDrawMatches(Canvas canvas);
-    
     private float scrollZoomGridScale(float invScale) {
-        float griddedInvScale = (int) (invScale * SCROLL_ZOOM_GRID) 
+        float griddedInvScale = (int) (invScale * SCROLL_ZOOM_GRID)
             / (float) SCROLL_ZOOM_GRID;
         return 1.0f / griddedInvScale;
     }
-    
+
     private float scrollZoomX(float scale) {
         int width = getViewWidth();
         float maxScrollZoomX = mContentWidth * scale - width;
@@ -2556,7 +2538,7 @@
         return -(maxScrollZoomY > 0 ? mZoomScrollY * maxScrollZoomY / maxY
                 : maxScrollZoomY / 2);
     }
-    
+
     private void drawMagnifyFrame(Canvas canvas, Rect frame, Paint paint) {
         final float ADORNMENT_LEN = 16.0f;
         float width = frame.width();
@@ -2577,13 +2559,13 @@
         path.offset(frame.left, frame.top);
         canvas.drawPath(path, paint);
     }
-    
-    // Returns frame surrounding magified portion of screen while 
+
+    // Returns frame surrounding magified portion of screen while
     // scroll-zoom is enabled. The frame is also used to center the
     // zoom-in zoom-out points at the start and end of the animation.
     private Rect scrollZoomFrame(int width, int height, float halfScale) {
         Rect scrollFrame = new Rect();
-        scrollFrame.set(mZoomScrollX, mZoomScrollY, 
+        scrollFrame.set(mZoomScrollX, mZoomScrollY,
                 mZoomScrollX + width, mZoomScrollY + height);
         if (mContentWidth * mZoomScrollLimit < width) {
             float scale = zoomFrameScaleX(width, halfScale, 1.0f);
@@ -2599,37 +2581,37 @@
         }
         return scrollFrame;
     }
-    
+
     private float zoomFrameScaleX(int width, float halfScale, float noScale) {
         // mContentWidth > width > mContentWidth * mZoomScrollLimit
         if (mContentWidth <= width) {
             return halfScale;
         }
-        float part = (width - mContentWidth * mZoomScrollLimit)  
+        float part = (width - mContentWidth * mZoomScrollLimit)
                 / (width * (1 - mZoomScrollLimit));
         return halfScale * part + noScale * (1.0f - part);
     }
-    
+
     private float zoomFrameScaleY(int height, float halfScale, float noScale) {
         if (mContentHeight <= height) {
             return halfScale;
         }
-        float part = (height - mContentHeight * mZoomScrollLimit)  
+        float part = (height - mContentHeight * mZoomScrollLimit)
                 / (height * (1 - mZoomScrollLimit));
         return halfScale * part + noScale * (1.0f - part);
     }
-    
+
     private float scrollZoomMagScale(float invScale) {
         return (invScale * 2 + mInvActualScale) / 3;
     }
-    
+
     private void scrollZoomDraw(Canvas canvas) {
-        float invScale = mZoomScrollInvLimit; 
+        float invScale = mZoomScrollInvLimit;
         int elapsed = 0;
         if (mTouchMode != SCROLL_ZOOM_OUT) {
-            elapsed = (int) Math.min(System.currentTimeMillis() 
+            elapsed = (int) Math.min(System.currentTimeMillis()
                 - mZoomScrollStart, SCROLL_ZOOM_DURATION);
-            float transitionScale = (mZoomScrollInvLimit - mInvActualScale) 
+            float transitionScale = (mZoomScrollInvLimit - mInvActualScale)
                     * elapsed / SCROLL_ZOOM_DURATION;
             if (mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
                 invScale = mInvActualScale + transitionScale;
@@ -2647,9 +2629,9 @@
             if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN) {
                 setHorizontalScrollBarEnabled(true);
                 setVerticalScrollBarEnabled(true);
-                updateTextEntry();
-                scrollTo((int) (scrollFrame.centerX() * mActualScale) 
-                        - (width >> 1), (int) (scrollFrame.centerY() 
+                rebuildWebTextView();
+                scrollTo((int) (scrollFrame.centerX() * mActualScale)
+                        - (width >> 1), (int) (scrollFrame.centerY()
                         * mActualScale) - (height >> 1));
                 mTouchMode = TOUCH_DONE_MODE;
             } else {
@@ -2658,10 +2640,10 @@
         }
         float newX = scrollZoomX(scale);
         float newY = scrollZoomY(scale);
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "scrollZoomDraw scale=" + scale + " + (" + newX
                     + ", " + newY + ") mZoomScroll=(" + mZoomScrollX + ", "
-                    + mZoomScrollY + ")" + " invScale=" + invScale + " scale=" 
+                    + mZoomScrollY + ")" + " invScale=" + invScale + " scale="
                     + scale);
         }
         canvas.translate(newX, newY);
@@ -2705,8 +2687,8 @@
         }
         canvas.scale(halfScale, halfScale, mZoomScrollX + width * halfX
                 , mZoomScrollY + height * halfY);
-        if (LOGV_ENABLED) {
-            Log.v(LOGTAG, "scrollZoomDraw halfScale=" + halfScale + " w/h=(" 
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "scrollZoomDraw halfScale=" + halfScale + " w/h=("
                     + width + ", " + height + ") half=(" + halfX + ", "
                     + halfY + ")");
         }
@@ -2734,7 +2716,7 @@
                 , Math.max(0, (int) ((x - left) / scale)));
         mZoomScrollY = Math.min(mContentHeight - height
                 , Math.max(0, (int) ((y - top) / scale)));
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "zoomScrollTap scale=" + scale + " + (" + left
                     + ", " + top + ") mZoomScroll=(" + mZoomScrollX + ", "
                     + mZoomScrollY + ")" + " x=" + x + " y=" + y);
@@ -2751,7 +2733,7 @@
         float y = (float) height / (float) mContentHeight;
         mZoomScrollLimit = Math.max(DEFAULT_MIN_ZOOM_SCALE, Math.min(x, y));
         mZoomScrollInvLimit = 1.0f / mZoomScrollLimit;
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "canZoomScrollOut"
                     + " mInvActualScale=" + mInvActualScale
                     + " mZoomScrollLimit=" + mZoomScrollLimit
@@ -2766,7 +2748,7 @@
         return mContentWidth >= width * limit
                 || mContentHeight >= height * limit;
     }
-        
+
     private void startZoomScrollOut() {
         setHorizontalScrollBarEnabled(false);
         setVerticalScrollBarEnabled(false);
@@ -2792,18 +2774,18 @@
         mZoomScrollStart = System.currentTimeMillis();
         Rect zoomFrame = scrollZoomFrame(width, height
                 , scrollZoomMagScale(mZoomScrollInvLimit));
-        mZoomScrollX = Math.max(0, (int) ((mScrollX + halfW) * mInvActualScale) 
+        mZoomScrollX = Math.max(0, (int) ((mScrollX + halfW) * mInvActualScale)
                 - (zoomFrame.width() >> 1));
-        mZoomScrollY = Math.max(0, (int) ((mScrollY + halfH) * mInvActualScale) 
+        mZoomScrollY = Math.max(0, (int) ((mScrollY + halfH) * mInvActualScale)
                 - (zoomFrame.height() >> 1));
         scrollTo(0, 0); // triggers inval, starts animation
         clearTextEntry();
-        if (LOGV_ENABLED) {
-            Log.v(LOGTAG, "startZoomScrollOut mZoomScroll=(" 
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "startZoomScrollOut mZoomScroll=("
                     + mZoomScrollX + ", " + mZoomScrollY +")");
         }
     }
-    
+
     private void zoomScrollOut() {
         if (canZoomScrollOut() == false) {
             mTouchMode = TOUCH_DONE_MODE;
@@ -2815,7 +2797,7 @@
     }
 
     private void moveZoomScrollWindow(float x, float y) {
-        if (Math.abs(x - mLastZoomScrollRawX) < 1.5f 
+        if (Math.abs(x - mLastZoomScrollRawX) < 1.5f
                 && Math.abs(y - mLastZoomScrollRawY) < 1.5f) {
             return;
         }
@@ -2827,12 +2809,12 @@
         int height = getViewHeight();
         int maxZoomX = mContentWidth - width;
         if (maxZoomX > 0) {
-            int maxScreenX = width - (int) Math.ceil(width 
+            int maxScreenX = width - (int) Math.ceil(width
                     * mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
-            if (LOGV_ENABLED) {
-                Log.v(LOGTAG, "moveZoomScrollWindow-X" 
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "moveZoomScrollWindow-X"
                         + " maxScreenX=" + maxScreenX + " width=" + width
-                        + " mZoomScrollLimit=" + mZoomScrollLimit + " x=" + x); 
+                        + " mZoomScrollLimit=" + mZoomScrollLimit + " x=" + x);
             }
             x += maxScreenX * mLastScrollX / maxZoomX - mLastTouchX;
             x *= Math.max(maxZoomX / maxScreenX, mZoomScrollInvLimit);
@@ -2840,12 +2822,12 @@
         }
         int maxZoomY = mContentHeight - height;
         if (maxZoomY > 0) {
-            int maxScreenY = height - (int) Math.ceil(height 
+            int maxScreenY = height - (int) Math.ceil(height
                     * mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
-            if (LOGV_ENABLED) {
-                Log.v(LOGTAG, "moveZoomScrollWindow-Y" 
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "moveZoomScrollWindow-Y"
                         + " maxScreenY=" + maxScreenY + " height=" + height
-                        + " mZoomScrollLimit=" + mZoomScrollLimit + " y=" + y); 
+                        + " mZoomScrollLimit=" + mZoomScrollLimit + " y=" + y);
             }
             y += maxScreenY * mLastScrollY / maxZoomY - mLastTouchY;
             y *= Math.max(maxZoomY / maxScreenY, mZoomScrollInvLimit);
@@ -2854,12 +2836,12 @@
         if (oldX != mZoomScrollX || oldY != mZoomScrollY) {
             invalidate();
         }
-        if (LOGV_ENABLED) {
-            Log.v(LOGTAG, "moveZoomScrollWindow" 
-                    + " scrollTo=(" + mZoomScrollX + ", " + mZoomScrollY + ")" 
-                    + " mLastTouch=(" + mLastTouchX + ", " + mLastTouchY + ")" 
-                    + " maxZoom=(" + maxZoomX + ", " + maxZoomY + ")" 
-                    + " last=("+mLastScrollX+", "+mLastScrollY+")" 
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "moveZoomScrollWindow"
+                    + " scrollTo=(" + mZoomScrollX + ", " + mZoomScrollY + ")"
+                    + " mLastTouch=(" + mLastTouchX + ", " + mLastTouchY + ")"
+                    + " maxZoom=(" + maxZoomX + ", " + maxZoomY + ")"
+                    + " last=("+mLastScrollX+", "+mLastScrollY+")"
                     + " x=" + x + " y=" + y);
         }
     }
@@ -2920,72 +2902,27 @@
         }
     }
 
-    /**
-     *  Class representing the node which is focused.
-     */
-    private static class FocusNode {
-        public FocusNode() {
-            mBounds = new Rect();
-        }
-        // Only to be called by JNI
-        private void setAll(boolean isTextField, boolean isTextArea, boolean 
-                isPassword, boolean isAnchor, boolean isRtlText, int maxLength, 
-                int textSize, int boundsX, int boundsY, int boundsRight, int 
-                boundsBottom, int nodePointer, int framePointer, String text, 
-                String name, int rootTextGeneration) {
-            mIsTextField        = isTextField;
-            mIsTextArea         = isTextArea;
-            mIsPassword         = isPassword;
-            mIsAnchor           = isAnchor;
-            mIsRtlText          = isRtlText;
-
-            mMaxLength          = maxLength;
-            mTextSize           = textSize;
-            
-            mBounds.set(boundsX, boundsY, boundsRight, boundsBottom);
-            
-            
-            mNodePointer        = nodePointer;
-            mFramePointer       = framePointer;
-            mText               = text;
-            mName               = name;
-            mRootTextGeneration = rootTextGeneration;
-        }
-        public boolean  mIsTextField;
-        public boolean  mIsTextArea;
-        public boolean  mIsPassword;
-        public boolean  mIsAnchor;
-        public boolean  mIsRtlText;
-
-        public int      mSelectionStart;
-        public int      mSelectionEnd;
-        public int      mMaxLength;
-        public int      mTextSize;
-        
-        public Rect     mBounds;
-        
-        public int      mNodePointer;
-        public int      mFramePointer;
-        public String   mText;
-        public String   mName;
-        public int      mRootTextGeneration;
+    WebViewCore.CursorData cursorData() {
+        WebViewCore.CursorData result = new WebViewCore.CursorData();
+        result.mMoveGeneration = nativeMoveGeneration();
+        result.mFrame = nativeCursorFramePointer();
+        result.mNode = nativeCursorNodePointer();
+        Rect bounds = nativeCursorNodeBounds();
+        result.mX = bounds.centerX();
+        result.mY = bounds.centerY();
+        return result;
     }
-    
-    // Warning: ONLY use mFocusNode AFTER calling nativeUpdateFocusNode(),
-    // and ONLY if it returns true;
-    private FocusNode mFocusNode = new FocusNode();
-    
+
     /**
      *  Delete text from start to end in the focused textfield. If there is no
-     *  focus, or if start == end, silently fail.  If start and end are out of 
+     *  focus, or if start == end, silently fail.  If start and end are out of
      *  order, swap them.
      *  @param  start   Beginning of selection to delete.
      *  @param  end     End of selection to delete.
      */
     /* package */ void deleteSelection(int start, int end) {
         mTextGeneration++;
-        mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, start, end,
-                new WebViewCore.FocusData(mFocusData));
+        mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, start, end);
     }
 
     /**
@@ -2995,123 +2932,121 @@
      *  @param  end     End of selection.
      */
     /* package */ void setSelection(int start, int end) {
-        mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end,
-                new WebViewCore.FocusData(mFocusData));
+        mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
     }
 
     // Called by JNI when a touch event puts a textfield into focus.
     private void displaySoftKeyboard() {
         InputMethodManager imm = (InputMethodManager)
                 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-        imm.showSoftInput(mTextEntry, 0);
-        mTextEntry.enableScrollOnScreen(true);
+        imm.showSoftInput(mWebTextView, 0);
+        mWebTextView.enableScrollOnScreen(true);
         // Now we need to fake a touch event to place the cursor where the
         // user touched.
         AbsoluteLayout.LayoutParams lp = (AbsoluteLayout.LayoutParams)
-                mTextEntry.getLayoutParams();
+                mWebTextView.getLayoutParams();
         if (lp != null) {
             // Take the last touch and adjust for the location of the
-            // TextDialog.
+            // WebTextView.
             float x = mLastTouchX + (float) (mScrollX - lp.x);
             float y = mLastTouchY + (float) (mScrollY - lp.y);
-            mTextEntry.fakeTouchEvent(x, y);
+            mWebTextView.fakeTouchEvent(x, y);
         }
     }
 
-    private void updateTextEntry() {
+    /*
+     * This method checks the current focus and potentially rebuilds
+     * mWebTextView to have the appropriate properties, such as password,
+     * multiline, and what text it contains.  It also removes it if necessary.
+     */
+    private void rebuildWebTextView() {
         // If we do not have focus, do nothing until we gain focus.
-        if (!hasFocus() && (null == mTextEntry || !mTextEntry.hasFocus())
-                || (mTouchMode >= FIRST_SCROLL_ZOOM 
+        if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())
+                || (mTouchMode >= FIRST_SCROLL_ZOOM
                 && mTouchMode <= LAST_SCROLL_ZOOM)) {
-            mNeedsUpdateTextEntry = true;
+            mNeedsRebuildWebTextView = true;
             return;
         }
         boolean alreadyThere = inEditingMode();
-        // inEditingMode can only return true if mTextEntry is non-null,
+        // inEditingMode can only return true if mWebTextView is non-null,
         // so we can safely call remove() if (alreadyThere)
-        if (0 == mNativeClass || !nativeUpdateFocusNode()) {
+        if (0 == mNativeClass || (!nativeFocusIsTextInput()
+                && !nativeCursorIsTextInput())) {
             if (alreadyThere) {
-                mTextEntry.remove();
-            }
-            return;
-        }
-        FocusNode node = mFocusNode;
-        if (!node.mIsTextField && !node.mIsTextArea) {
-            if (alreadyThere) {
-                mTextEntry.remove();
+                mWebTextView.remove();
             }
             return;
         }
         // At this point, we know we have found an input field, so go ahead
-        // and create the TextDialog if necessary.
-        if (mTextEntry == null) {
-            mTextEntry = new TextDialog(mContext, WebView.this);
+        // and create the WebTextView if necessary.
+        if (mWebTextView == null) {
+            mWebTextView = new WebTextView(mContext, WebView.this);
             // Initialize our generation number.
             mTextGeneration = 0;
         }
-        mTextEntry.setTextSize(contentToView(node.mTextSize));
+        mWebTextView.setTextSize(contentToView(nativeFocusTextSize()));
         Rect visibleRect = sendOurVisibleRect();
         // Note that sendOurVisibleRect calls viewToContent, so the coordinates
         // should be in content coordinates.
-        if (!Rect.intersects(node.mBounds, visibleRect)) {
+        Rect bounds = nativeFocusNodeBounds();
+        if (!Rect.intersects(bounds, visibleRect)) {
             // Node is not on screen, so do not bother.
             return;
         }
-        int x = node.mBounds.left;
-        int y = node.mBounds.top;
-        int width = node.mBounds.width();
-        int height = node.mBounds.height();
-        if (alreadyThere && mTextEntry.isSameTextField(node.mNodePointer)) {
+        String text = nativeFocusText();
+        int nodePointer = nativeFocusNodePointer();
+        if (alreadyThere && mWebTextView.isSameTextField(nodePointer)) {
             // It is possible that we have the same textfield, but it has moved,
             // i.e. In the case of opening/closing the screen.
             // In that case, we need to set the dimensions, but not the other
             // aspects.
             // We also need to restore the selection, which gets wrecked by
             // calling setTextEntryRect.
-            Spannable spannable = (Spannable) mTextEntry.getText();
+            Spannable spannable = (Spannable) mWebTextView.getText();
             int start = Selection.getSelectionStart(spannable);
             int end = Selection.getSelectionEnd(spannable);
-            setTextEntryRect(x, y, width, height);
             // If the text has been changed by webkit, update it.  However, if
             // there has been more UI text input, ignore it.  We will receive
             // another update when that text is recognized.
-            if (node.mText != null && !node.mText.equals(spannable.toString())
-                    && node.mRootTextGeneration == mTextGeneration) {
-                mTextEntry.setTextAndKeepSelection(node.mText);
+            if (text != null && !text.equals(spannable.toString())
+                    && nativeTextGeneration() == mTextGeneration) {
+                mWebTextView.setTextAndKeepSelection(text);
             } else {
                 Selection.setSelection(spannable, start, end);
             }
         } else {
-            String text = node.mText;
-            setTextEntryRect(x, y, width, height);
-            mTextEntry.setGravity(node.mIsRtlText ? Gravity.RIGHT : 
+            Rect vBox = contentToView(bounds);
+            mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
+                    vBox.height());
+            mWebTextView.setGravity(nativeFocusIsRtlText() ? Gravity.RIGHT :
                     Gravity.NO_GRAVITY);
             // this needs to be called before update adapter thread starts to
-            // ensure the mTextEntry has the same node pointer
-            mTextEntry.setNodePointer(node.mNodePointer);
+            // ensure the mWebTextView has the same node pointer
+            mWebTextView.setNodePointer(nodePointer);
             int maxLength = -1;
-            if (node.mIsTextField) {
-                maxLength = node.mMaxLength;
+            boolean isTextField = nativeFocusIsTextField();
+            if (isTextField) {
+                maxLength = nativeFocusMaxLength();
+                String name = nativeFocusName();
                 if (mWebViewCore.getSettings().getSaveFormData()
-                        && node.mName != null) {
+                        && name != null) {
                     HashMap data = new HashMap();
-                    data.put("text", node.mText);
+                    data.put("text", text);
                     Message update = mPrivateHandler.obtainMessage(
-                            UPDATE_TEXT_ENTRY_ADAPTER, node.mNodePointer, 0,
-                            data);
-                    UpdateTextEntryAdapter updater = new UpdateTextEntryAdapter(
-                            node.mName, getUrl(), update);
+                            REQUEST_FORM_DATA, nodePointer, 0, data);
+                    RequestFormData updater = new RequestFormData(name,
+                            getUrl(), update);
                     Thread t = new Thread(updater);
                     t.start();
                 }
             }
-            mTextEntry.setMaxLength(maxLength);
+            mWebTextView.setMaxLength(maxLength);
             AutoCompleteAdapter adapter = null;
-            mTextEntry.setAdapterCustom(adapter);
-            mTextEntry.setSingleLine(node.mIsTextField);
-            mTextEntry.setInPassword(node.mIsPassword);
+            mWebTextView.setAdapterCustom(adapter);
+            mWebTextView.setSingleLine(isTextField);
+            mWebTextView.setInPassword(nativeFocusIsPassword());
             if (null == text) {
-                mTextEntry.setText("", 0, 0);
+                mWebTextView.setText("", 0, 0);
             } else {
                 // Change to true to enable the old style behavior, where
                 // entering a textfield/textarea always set the selection to the
@@ -3122,24 +3057,29 @@
                 // textarea.  Testing out a new behavior, where textfields set
                 // selection at the end, and textareas at the beginning.
                 if (false) {
-                    mTextEntry.setText(text, 0, text.length());
-                } else if (node.mIsTextField) {
+                    mWebTextView.setText(text, 0, text.length());
+                } else if (isTextField) {
                     int length = text.length();
-                    mTextEntry.setText(text, length, length);
+                    mWebTextView.setText(text, length, length);
                 } else {
-                    mTextEntry.setText(text, 0, 0);
+                    mWebTextView.setText(text, 0, 0);
                 }
             }
-            mTextEntry.requestFocus();
+            mWebTextView.requestFocus();
         }
     }
 
-    private class UpdateTextEntryAdapter implements Runnable {
+    /*
+     * This class requests an Adapter for the WebTextView which shows past
+     * entries stored in the database.  It is a Runnable so that it can be done
+     * in its own thread, without slowing down the UI.
+     */
+    private class RequestFormData implements Runnable {
         private String mName;
         private String mUrl;
         private Message mUpdateMessage;
 
-        public UpdateTextEntryAdapter(String name, String url, Message msg) {
+        public RequestFormData(String name, String url, Message msg) {
             mName = name;
             mUrl = url;
             mUpdateMessage = msg;
@@ -3156,21 +3096,13 @@
         }
     }
 
-    private void setTextEntryRect(int x, int y, int width, int height) {
-        x = contentToView(x);
-        y = contentToView(y);
-        width = contentToView(width);
-        height = contentToView(height);
-        mTextEntry.setRect(x, y, width, height);
-    }
-
-    // This is used to determine long press with the enter key, or
-    // a center key.  Does not affect long press with the trackball/touch.
-    private boolean mGotEnterDown = false;
+    // This is used to determine long press with the center key.  Does not
+    // affect long press with the trackball/touch.
+    private boolean mGotCenterDown = false;
 
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
                     + ", " + event);
         }
@@ -3198,15 +3130,15 @@
             return false;
         }
 
-        if (mShiftIsPressed == false && nativeFocusNodeWantsKeyEvents() == false
-                && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT 
+        if (mShiftIsPressed == false && nativeCursorWantsKeyEvents() == false
+                && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
                 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)) {
             mExtendSelection = false;
             mShiftIsPressed = true;
-            if (nativeUpdateFocusNode()) {
-                FocusNode node = mFocusNode;
-                mSelectX = contentToView(node.mBounds.left);
-                mSelectY = contentToView(node.mBounds.top);
+            if (nativeHasCursorNode()) {
+                Rect rect = nativeCursorNodeBounds();
+                mSelectX = contentToView(rect.left);
+                mSelectY = contentToView(rect.top);
             } else {
                 mSelectX = mScrollX + (int) mLastTouchX;
                 mSelectY = mScrollY + (int) mLastTouchY;
@@ -3228,13 +3160,12 @@
             return false;
         }
 
-        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
-                || keyCode == KeyEvent.KEYCODE_ENTER) {
+        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
             switchOutDrawHistory();
             if (event.getRepeatCount() == 0) {
-                mGotEnterDown = true;
+                mGotCenterDown = true;
                 mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                        .obtainMessage(LONG_PRESS_ENTER), LONG_PRESS_TIMEOUT);
+                        .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
                 // Already checked mNativeClass, so we do not need to check it
                 // again.
                 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
@@ -3282,7 +3213,7 @@
         }
 
         // TODO: should we pass all the keys to DOM or check the meta tag
-        if (nativeFocusNodeWantsKeyEvents() || true) {
+        if (nativeCursorWantsKeyEvents() || true) {
             // pass the key to DOM
             mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
             // return true as DOM handles the key
@@ -3295,7 +3226,7 @@
 
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
                     + ", " + event);
         }
@@ -3305,10 +3236,9 @@
         }
 
         // special CALL handling when focus node's href is "tel:XXX"
-        if (keyCode == KeyEvent.KEYCODE_CALL && nativeUpdateFocusNode()) {
-            FocusNode node = mFocusNode;
-            String text = node.mText;
-            if (!node.mIsTextField && !node.mIsTextArea && text != null
+        if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
+            String text = nativeCursorText();
+            if (!nativeCursorIsTextInput() && text != null
                     && text.startsWith(SCHEME_TEL)) {
                 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
                 getContext().startActivity(intent);
@@ -3335,7 +3265,7 @@
             return false;
         }
 
-        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT 
+        if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
                 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
             if (commitCopy()) {
                 return true;
@@ -3349,55 +3279,30 @@
             return false;
         }
 
-        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
-                || keyCode == KeyEvent.KEYCODE_ENTER) {
+        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
             // remove the long press message first
-            mPrivateHandler.removeMessages(LONG_PRESS_ENTER);
-            mGotEnterDown = false;
+            mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
+            mGotCenterDown = false;
 
-            if (KeyEvent.KEYCODE_DPAD_CENTER == keyCode) {
-                if (mShiftIsPressed) {
-                    return false;
-                }
-                if (getSettings().supportZoom()) {
-                    if (mTouchMode == TOUCH_DOUBLECLICK_MODE) {
-                        zoomScrollOut();
-                    } else {
-                        if (LOGV_ENABLED) {
-                            Log.v(LOGTAG, "TOUCH_DOUBLECLICK_MODE");
-                        }
-                        mPrivateHandler.sendMessageDelayed(mPrivateHandler
-                                .obtainMessage(SWITCH_TO_ENTER), TAP_TIMEOUT);
-                        mTouchMode = TOUCH_DOUBLECLICK_MODE;
-                    }
-                    return true;
-                }
+            if (mShiftIsPressed) {
+                return false;
             }
-
-            Rect visibleRect = sendOurVisibleRect();
-            // Note that sendOurVisibleRect calls viewToContent, so the
-            // coordinates should be in content coordinates.
-            if (nativeUpdateFocusNode()) {
-                if (Rect.intersects(mFocusNode.mBounds, visibleRect)) {
-                    nativeSetFollowedLink(true);
-                    mWebViewCore.sendMessage(EventHub.SET_FINAL_FOCUS,
-                            EventHub.BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP, 0,
-                            new WebViewCore.FocusData(mFocusData));
-                    playSoundEffect(SoundEffectConstants.CLICK);
-                    if (!mCallbackProxy.uiOverrideUrlLoading(mFocusNode.mText)) {
-                        // use CLICK instead of KEY_DOWN/KEY_UP so that we can
-                        // trigger mouse click events
-                        mWebViewCore.sendMessage(EventHub.CLICK);
-                    }
+            if (getSettings().supportZoom()
+                    && mTouchMode == TOUCH_DOUBLECLICK_MODE) {
+                zoomScrollOut();
+            } else {
+                mPrivateHandler.sendMessageDelayed(mPrivateHandler
+                        .obtainMessage(SWITCH_TO_CLICK), TAP_TIMEOUT);
+                if (DebugFlags.WEB_VIEW) {
+                    Log.v(LOGTAG, "TOUCH_DOUBLECLICK_MODE");
                 }
-                return true;
+                mTouchMode = TOUCH_DOUBLECLICK_MODE;
             }
-            // Bubble up the key event as WebView doesn't handle it
-            return false;
+            return true;
         }
 
         // TODO: should we pass all the keys to DOM or check the meta tag
-        if (nativeFocusNodeWantsKeyEvents() || true) {
+        if (nativeCursorWantsKeyEvents() || true) {
             // pass the key to DOM
             mWebViewCore.sendMessage(EventHub.KEY_UP, event);
             // return true as DOM handles the key
@@ -3407,7 +3312,7 @@
         // Bubble up the key event as WebView doesn't handle it
         return false;
     }
-    
+
     /**
      * @hide
      */
@@ -3464,15 +3369,15 @@
         // Clean up the zoom controller
         mZoomButtonsController.setVisible(false);
     }
-    
+
     // Implementation for OnHierarchyChangeListener
     public void onChildViewAdded(View parent, View child) {}
-    
+
     public void onChildViewRemoved(View p, View child) {
         if (child == this) {
             if (inEditingMode()) {
                 clearTextEntry();
-                mNeedsUpdateTextEntry = true;
+                mNeedsRebuildWebTextView = true;
             }
         }
     }
@@ -3486,18 +3391,18 @@
     public void onGlobalFocusChanged(View oldFocus, View newFocus) {
     }
 
-    // To avoid drawing the focus ring, and remove the TextView when our window
+    // To avoid drawing the cursor ring, and remove the TextView when our window
     // loses focus.
     @Override
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         if (hasWindowFocus) {
             if (hasFocus()) {
                 // If our window regained focus, and we have focus, then begin
-                // drawing the focus ring, and restore the TextView if
+                // drawing the cursor ring, and restore the TextView if
                 // necessary.
-                mDrawFocusRing = true;
-                if (mNeedsUpdateTextEntry) {
-                    updateTextEntry();
+                mDrawCursorRing = true;
+                if (mNeedsRebuildWebTextView) {
+                    rebuildWebTextView();
                 }
                 if (mNativeClass != 0) {
                     nativeRecordButtons(true, false, true);
@@ -3505,8 +3410,8 @@
                 setFocusControllerActive(true);
             } else {
                 // If our window gained focus, but we do not have it, do not
-                // draw the focus ring.
-                mDrawFocusRing = false;
+                // draw the cursor ring.
+                mDrawCursorRing = false;
                 // We do not call nativeRecordButtons here because we assume
                 // that when we lost focus, or window focus, it got called with
                 // false for the first parameter
@@ -3515,13 +3420,13 @@
             if (getSettings().getBuiltInZoomControls() && !mZoomButtonsController.isVisible()) {
                 /*
                  * The zoom controls come in their own window, so our window
-                 * loses focus. Our policy is to not draw the focus ring if
+                 * loses focus. Our policy is to not draw the cursor ring if
                  * our window is not focused, but this is an exception since
                  * the user can still navigate the web page with the zoom
                  * controls showing.
                  */
-                // If our window has lost focus, stop drawing the focus ring
-                mDrawFocusRing = false;
+                // If our window has lost focus, stop drawing the cursor ring
+                mDrawCursorRing = false;
             }
             mGotKeyDown = false;
             mShiftIsPressed = false;
@@ -3547,23 +3452,23 @@
     @Override
     protected void onFocusChanged(boolean focused, int direction,
             Rect previouslyFocusedRect) {
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
         }
         if (focused) {
             // When we regain focus, if we have window focus, resume drawing
-            // the focus ring, and add the TextView if necessary.
+            // the cursor ring, and add the TextView if necessary.
             if (hasWindowFocus()) {
-                mDrawFocusRing = true;
-                if (mNeedsUpdateTextEntry) {
-                    updateTextEntry();
-                    mNeedsUpdateTextEntry = false;
+                mDrawCursorRing = true;
+                if (mNeedsRebuildWebTextView) {
+                    rebuildWebTextView();
+                    mNeedsRebuildWebTextView = false;
                 }
                 if (mNativeClass != 0) {
                     nativeRecordButtons(true, false, true);
                 }
                 // FIXME: This is unnecessary if we are gaining focus from the
-                // TextDialog.  How can we tell if it was the last thing in
+                // WebTextView.  How can we tell if it was the last thing in
                 // focus?
                 setFocusControllerActive(true);
             //} else {
@@ -3573,9 +3478,9 @@
             }
         } else {
             // When we lost focus, unless focus went to the TextView (which is
-            // true if we are in editing mode), stop drawing the focus ring.
+            // true if we are in editing mode), stop drawing the cursor ring.
             if (!inEditingMode()) {
-                mDrawFocusRing = false;
+                mDrawCursorRing = false;
                 if (mNativeClass != 0) {
                     nativeRecordButtons(false, false, true);
                 }
@@ -3610,8 +3515,8 @@
         super.onScrollChanged(l, t, oldl, oldt);
         sendOurVisibleRect();
     }
-    
-    
+
+
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
         boolean dispatch = true;
@@ -3655,7 +3560,7 @@
             return false;
         }
 
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="
                     + mTouchMode);
         }
@@ -3680,7 +3585,7 @@
         if (mForwardTouchEvents && mTouchMode != SCROLL_ZOOM_OUT
                 && mTouchMode != SCROLL_ZOOM_ANIMATION_IN
                 && mTouchMode != SCROLL_ZOOM_ANIMATION_OUT
-                && (action != MotionEvent.ACTION_MOVE || 
+                && (action != MotionEvent.ACTION_MOVE ||
                         eventTime - mLastSentTouchTime > TOUCH_SENT_INTERVAL)) {
             WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
             ted.mAction = action;
@@ -3711,7 +3616,7 @@
                     mSelectX = mScrollX + (int) x;
                     mSelectY = mScrollY + (int) y;
                     mTouchMode = TOUCH_SELECT_MODE;
-                    if (LOGV_ENABLED) {
+                    if (DebugFlags.WEB_VIEW) {
                         Log.v(LOGTAG, "select=" + mSelectX + "," + mSelectY);
                     }
                     nativeMoveSelection(viewToContent(mSelectX)
@@ -3739,7 +3644,7 @@
                 break;
             }
             case MotionEvent.ACTION_MOVE: {
-                if (mTouchMode == TOUCH_DONE_MODE 
+                if (mTouchMode == TOUCH_DONE_MODE
                         || mTouchMode == SCROLL_ZOOM_ANIMATION_IN
                         || mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
                     // no dragging during scroll zoom animation
@@ -3756,7 +3661,7 @@
                     if (mTouchMode == TOUCH_SELECT_MODE) {
                         mSelectX = mScrollX + (int) x;
                         mSelectY = mScrollY + (int) y;
-                        if (LOGV_ENABLED) {
+                        if (DebugFlags.WEB_VIEW) {
                             Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY);
                         }
                         nativeMoveSelection(viewToContent(mSelectX)
@@ -3801,7 +3706,7 @@
                     if (settings.supportZoom()
                             && settings.getBuiltInZoomControls()
                             && !mZoomButtonsController.isVisible()
-                            && (canZoomScrollOut() || 
+                            && (canZoomScrollOut() ||
                                     mMinZoomScale < mMaxZoomScale)) {
                         mZoomButtonsController.setVisible(true);
                     }
@@ -3827,7 +3732,7 @@
                             }
                             // reverse direction means lock in the snap mode
                             if ((ax > MAX_SLOPE_FOR_DIAG * ay) &&
-                                    ((mSnapPositive && 
+                                    ((mSnapPositive &&
                                     deltaX < -mMinLockSnapReverseDistance)
                                     || (!mSnapPositive &&
                                     deltaX > mMinLockSnapReverseDistance))) {
@@ -3841,9 +3746,9 @@
                             }
                             // reverse direction means lock in the snap mode
                             if ((ay > MAX_SLOPE_FOR_DIAG * ax) &&
-                                    ((mSnapPositive && 
+                                    ((mSnapPositive &&
                                     deltaY < -mMinLockSnapReverseDistance)
-                                    || (!mSnapPositive && 
+                                    || (!mSnapPositive &&
                                     deltaY > mMinLockSnapReverseDistance))) {
                                 mSnapScrollMode = SNAP_Y_LOCK;
                             }
@@ -3908,7 +3813,7 @@
                         // no action during scroll animation
                         break;
                     case SCROLL_ZOOM_OUT:
-                        if (LOGV_ENABLED) {
+                        if (DebugFlags.WEB_VIEW) {
                             Log.v(LOGTAG, "ACTION_UP SCROLL_ZOOM_OUT"
                                     + " eventTime - mLastTouchTime="
                                     + (eventTime - mLastTouchTime));
@@ -3974,7 +3879,7 @@
         }
         return true;
     }
-    
+
     private long mTrackballFirstTime = 0;
     private long mTrackballLastTime = 0;
     private float mTrackballRemainsX = 0.0f;
@@ -4000,10 +3905,10 @@
     private Rect mLastFocusBounds;
 
     // Set by default; BrowserActivity clears to interpret trackball data
-    // directly for movement. Currently, the framework only passes 
+    // directly for movement. Currently, the framework only passes
     // arrow key events, not trackball events, from one child to the next
     private boolean mMapTrackballToArrowKeys = true;
-    
+
     public void setMapTrackballToArrowKeys(boolean setMap) {
         mMapTrackballToArrowKeys = setMap;
     }
@@ -4021,26 +3926,26 @@
             return true;
         }
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mPrivateHandler.removeMessages(SWITCH_TO_ENTER);
+            mPrivateHandler.removeMessages(SWITCH_TO_CLICK);
             mTrackballDown = true;
             if (mNativeClass != 0) {
                 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
             }
             if (time - mLastFocusTime <= TRACKBALL_TIMEOUT
-                    && !mLastFocusBounds.equals(nativeGetFocusRingBounds())) {
+                    && !mLastFocusBounds.equals(nativeGetCursorRingBounds())) {
                 nativeSelectBestAt(mLastFocusBounds);
             }
-            if (LOGV_ENABLED) {
+            if (DebugFlags.WEB_VIEW) {
                 Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
-                        + " time=" + time 
+                        + " time=" + time
                         + " mLastFocusTime=" + mLastFocusTime);
             }
             if (isInTouchMode()) requestFocusFromTouch();
             return false; // let common code in onKeyDown at it
-        } 
+        }
         if (ev.getAction() == MotionEvent.ACTION_UP) {
-            // LONG_PRESS_ENTER is set in common onKeyDown
-            mPrivateHandler.removeMessages(LONG_PRESS_ENTER);
+            // LONG_PRESS_CENTER is set in common onKeyDown
+            mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
             mTrackballDown = false;
             mTrackballUpTime = time;
             if (mShiftIsPressed) {
@@ -4050,42 +3955,42 @@
                     mExtendSelection = true;
                 }
             }
-            if (LOGV_ENABLED) {
+            if (DebugFlags.WEB_VIEW) {
                 Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
-                        + " time=" + time 
+                        + " time=" + time
                 );
             }
             return false; // let common code in onKeyUp at it
         }
         if (mMapTrackballToArrowKeys && mShiftIsPressed == false) {
-            if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent gmail quit");
+            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
             return false;
         }
-        // no move if we're still waiting on SWITCH_TO_ENTER timeout
+        // no move if we're still waiting on SWITCH_TO_CLICK timeout
         if (mTouchMode == TOUCH_DOUBLECLICK_MODE) {
-            if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent 2 click quit");
+            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent 2 click quit");
             return true;
         }
         if (mTrackballDown) {
-            if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent down quit");
+            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
             return true; // discard move if trackball is down
         }
         if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
-            if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
+            if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
             return true;
         }
         // TODO: alternatively we can do panning as touch does
         switchOutDrawHistory();
         if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
-            if (LOGV_ENABLED) {
-                Log.v(LOGTAG, "onTrackballEvent time=" 
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "onTrackballEvent time="
                         + time + " last=" + mTrackballLastTime);
             }
             mTrackballFirstTime = time;
             mTrackballXMove = mTrackballYMove = 0;
         }
         mTrackballLastTime = time;
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
         }
         mTrackballRemainsX += ev.getX();
@@ -4093,7 +3998,7 @@
         doTrackball(time);
         return true;
     }
-    
+
     void moveSelection(float xRate, float yRate) {
         if (mNativeClass == 0)
             return;
@@ -4107,8 +4012,8 @@
                 , mSelectX));
         mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
                 , mSelectY));
-        if (LOGV_ENABLED) {
-            Log.v(LOGTAG, "moveSelection" 
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "moveSelection"
                     + " mSelectX=" + mSelectX
                     + " mSelectY=" + mSelectY
                     + " mScrollX=" + mScrollX
@@ -4120,10 +4025,10 @@
         nativeMoveSelection(viewToContent(mSelectX)
                 , viewToContent(mSelectY), mExtendSelection);
         int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
-                : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET 
+                : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
                 : 0;
         int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET
-                : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET 
+                : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
                 : 0;
         pinScrollBy(scrollX, scrollY, true, 0);
         Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1);
@@ -4180,7 +4085,7 @@
         if (elapsed == 0) {
             elapsed = TRACKBALL_TIMEOUT;
         }
-        float xRate = mTrackballRemainsX * 1000 / elapsed; 
+        float xRate = mTrackballRemainsX * 1000 / elapsed;
         float yRate = mTrackballRemainsY * 1000 / elapsed;
         if (mShiftIsPressed) {
             moveSelection(xRate, yRate);
@@ -4190,7 +4095,7 @@
         float ax = Math.abs(xRate);
         float ay = Math.abs(yRate);
         float maxA = Math.max(ax, ay);
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
                     + " xRate=" + xRate
                     + " yRate=" + yRate
@@ -4207,9 +4112,9 @@
             int maxWH = Math.max(width, height);
             mZoomScrollX += scaleTrackballX(xRate, maxWH);
             mZoomScrollY += scaleTrackballY(yRate, maxWH);
-            if (LOGV_ENABLED) {
+            if (DebugFlags.WEB_VIEW) {
                 Log.v(LOGTAG, "doTrackball SCROLL_ZOOM_OUT"
-                        + " mZoomScrollX=" + mZoomScrollX 
+                        + " mZoomScrollX=" + mZoomScrollX
                         + " mZoomScrollY=" + mZoomScrollY);
             }
             mZoomScrollX = Math.min(width, Math.max(0, mZoomScrollX));
@@ -4227,13 +4132,13 @@
         int oldScrollX = mScrollX;
         int oldScrollY = mScrollY;
         if (count > 0) {
-            int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ? 
-                    KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN : 
+            int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
+                    KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
                     mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
                     KeyEvent.KEYCODE_DPAD_RIGHT;
             count = Math.min(count, TRACKBALL_MOVE_COUNT);
-            if (LOGV_ENABLED) {
-                Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode 
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
                         + " count=" + count
                         + " mTrackballRemainsX=" + mTrackballRemainsX
                         + " mTrackballRemainsY=" + mTrackballRemainsY);
@@ -4246,12 +4151,12 @@
         if (count >= TRACKBALL_SCROLL_COUNT) {
             int xMove = scaleTrackballX(xRate, width);
             int yMove = scaleTrackballY(yRate, height);
-            if (LOGV_ENABLED) {
+            if (DebugFlags.WEB_VIEW) {
                 Log.v(LOGTAG, "doTrackball pinScrollBy"
                         + " count=" + count
                         + " xMove=" + xMove + " yMove=" + yMove
-                        + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX) 
-                        + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY) 
+                        + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
+                        + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
                         );
             }
             if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
@@ -4264,18 +4169,18 @@
                 pinScrollBy(xMove, yMove, true, 0);
             }
             mUserScroll = true;
-        } 
-        mWebViewCore.sendMessage(EventHub.UNBLOCK_FOCUS);        
+        }
+        mWebViewCore.sendMessage(EventHub.UNBLOCK_FOCUS);
     }
 
     public void flingScroll(int vx, int vy) {
         int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
         int maxY = Math.max(computeVerticalScrollRange() - getViewHeight(), 0);
-        
+
         mScroller.fling(mScrollX, mScrollY, vx, vy, 0, maxX, 0, maxY);
         invalidate();
     }
-    
+
     private void doFling() {
         if (mVelocityTracker == null) {
             return;
@@ -4294,7 +4199,7 @@
                 vx = 0;
             }
         }
-        
+
         if (true /* EMG release: make our fling more like Maps' */) {
             // maps cuts their velocity in half
             vx = vx * 3 / 4;
@@ -4355,7 +4260,7 @@
         }
         if (mZoomControls == null) {
             mZoomControls = createZoomControls();
-            
+
             /*
              * need to be set to VISIBLE first so that getMeasuredHeight() in
              * {@link #onSizeChanged()} can return the measured value for proper
@@ -4364,7 +4269,7 @@
             mZoomControls.setVisibility(View.VISIBLE);
             mZoomControlRunnable = new Runnable() {
                 public void run() {
-                    
+
                     /* Don't dismiss the controls if the user has
                      * focus on them. Wait and check again later.
                      */
@@ -4416,7 +4321,7 @@
     /**
      * Gets the {@link ZoomButtonsController} which can be used to add
      * additional buttons to the zoom controls window.
-     * 
+     *
      * @return The instance of {@link ZoomButtonsController} used by this class,
      *         or null if it is unavailable.
      * @hide
@@ -4459,7 +4364,7 @@
 
     /*package*/ void shortPressOnTextField() {
         if (inEditingMode()) {
-            View v = mTextEntry;
+            View v = mWebTextView;
             int x = viewToContent((v.getLeft() + v.getRight()) >> 1);
             int y = viewToContent((v.getTop() + v.getBottom()) >> 1);
             nativeMotionUp(x, y, mNavSlop);
@@ -4480,8 +4385,7 @@
                         Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0);
             }
         }
-        if (nativeUpdateFocusNode() && !mFocusNode.mIsTextField
-                && !mFocusNode.mIsTextArea) {
+        if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
             playSoundEffect(SoundEffectConstants.CLICK);
         }
     }
@@ -4496,7 +4400,8 @@
     public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
         boolean result = false;
         if (inEditingMode()) {
-            result = mTextEntry.requestFocus(direction, previouslyFocusedRect);
+            result = mWebTextView.requestFocus(direction,
+                    previouslyFocusedRect);
         } else {
             result = super.requestFocus(direction, previouslyFocusedRect);
             if (mWebViewCore.getSettings().getNeedInitialFocus()) {
@@ -4520,7 +4425,7 @@
                     default:
                         return result;
                 }
-                if (mNativeClass != 0 && !nativeUpdateFocusNode()) {
+                if (mNativeClass != 0 && !nativeHasCursorNode()) {
                     navHandledKey(fakeKeyDirection, 1, true, 0);
                 }
             }
@@ -4626,11 +4531,10 @@
 
         return false;
     }
-    
+
     /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
             String replace, int newStart, int newEnd) {
         HashMap arg = new HashMap();
-        arg.put("focusData", new WebViewCore.FocusData(mFocusData));
         arg.put("replace", replace);
         arg.put("start", Integer.valueOf(newStart));
         arg.put("end", Integer.valueOf(newEnd));
@@ -4640,19 +4544,18 @@
 
     /* package */ void passToJavaScript(String currentText, KeyEvent event) {
         HashMap arg = new HashMap();
-        arg.put("focusData", new WebViewCore.FocusData(mFocusData));
         arg.put("event", event);
         arg.put("currentText", currentText);
         // Increase our text generation number, and pass it to webcore thread
         mTextGeneration++;
         mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
         // WebKit's document state is not saved until about to leave the page.
-        // To make sure the host application, like Browser, has the up to date 
-        // document state when it goes to background, we force to save the 
+        // To make sure the host application, like Browser, has the up to date
+        // document state when it goes to background, we force to save the
         // document state.
         mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
         mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
-                new WebViewCore.FocusData(mFocusData), 1000);
+                cursorData(), 1000);
     }
 
     /* package */ WebViewCore getWebViewCore() {
@@ -4670,9 +4573,9 @@
     class PrivateHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
-            if (LOGV_ENABLED) {
-                Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what 
-                        > INVAL_RECT_MSG_ID ? Integer.toString(msg.what) 
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
+                        > INVAL_RECT_MSG_ID ? Integer.toString(msg.what)
                         : HandlerDebugString[msg.what - REMEMBER_PASSWORD]);
             }
             switch (msg.what) {
@@ -4700,22 +4603,31 @@
                 case SWITCH_TO_LONGPRESS: {
                     mTouchMode = TOUCH_DONE_MODE;
                     performLongClick();
-                    updateTextEntry();
+                    rebuildWebTextView();
                     break;
                 }
-                case SWITCH_TO_ENTER:
-                    if (LOGV_ENABLED) Log.v(LOGTAG, "SWITCH_TO_ENTER");
+                case SWITCH_TO_CLICK:
                     mTouchMode = TOUCH_DONE_MODE;
-                    onKeyUp(KeyEvent.KEYCODE_ENTER
-                            , new KeyEvent(KeyEvent.ACTION_UP
-                            , KeyEvent.KEYCODE_ENTER));
+                    Rect visibleRect = sendOurVisibleRect();
+                    // Note that sendOurVisibleRect calls viewToContent, so the
+                    // coordinates should be in content coordinates.
+                    if (!nativeCursorIntersects(visibleRect)) {
+                        break;
+                    }
+                    nativeSetFollowedLink(true);
+                    mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
+                            cursorData());
+                    playSoundEffect(SoundEffectConstants.CLICK);
+                    if (!mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
+                        mWebViewCore.sendMessage(EventHub.CLICK);
+                    }
                     break;
                 case SCROLL_BY_MSG_ID:
                     setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj);
                     break;
                 case SYNC_SCROLL_TO_MSG_ID:
                     if (mUserScroll) {
-                        // if user has scrolled explicitly, don't sync the 
+                        // if user has scrolled explicitly, don't sync the
                         // scroll position any more
                         mUserScroll = false;
                         break;
@@ -4724,7 +4636,7 @@
                 case SCROLL_TO_MSG_ID:
                     if (setContentScrollTo(msg.arg1, msg.arg2)) {
                         // if we can't scroll to the exact position due to pin,
-                        // send a message to WebCore to re-scroll when we get a 
+                        // send a message to WebCore to re-scroll when we get a
                         // new picture
                         mUserScroll = false;
                         mWebViewCore.sendMessage(EventHub.SYNC_SCROLL,
@@ -4736,7 +4648,7 @@
                     break;
                 case NEW_PICTURE_MSG_ID:
                     // called for new content
-                    final WebViewCore.DrawData draw = 
+                    final WebViewCore.DrawData draw =
                             (WebViewCore.DrawData) msg.obj;
                     final Point viewSize = draw.mViewPoint;
                     if (mZoomScale > 0) {
@@ -4759,9 +4671,9 @@
                     // received in the fixed dimension.
                     final boolean updateLayout = viewSize.x == mLastWidthSent
                             && viewSize.y == mLastHeightSent;
-                    recordNewContentSize(draw.mWidthHeight.x, 
+                    recordNewContentSize(draw.mWidthHeight.x,
                             draw.mWidthHeight.y, updateLayout);
-                    if (LOGV_ENABLED) {
+                    if (DebugFlags.WEB_VIEW) {
                         Rect b = draw.mInvalRegion.getBounds();
                         Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
                                 b.left+","+b.top+","+b.right+","+b.bottom+"}");
@@ -4777,22 +4689,23 @@
                     break;
                 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
                     // Make sure that the textfield is currently focused
-                    // and representing the same node as the pointer.  
-                    if (inEditingMode() && 
-                            mTextEntry.isSameTextField(msg.arg1)) {
+                    // and representing the same node as the pointer.
+                    if (inEditingMode() &&
+                            mWebTextView.isSameTextField(msg.arg1)) {
                         if (msg.getData().getBoolean("password")) {
-                            Spannable text = (Spannable) mTextEntry.getText();
+                            Spannable text = (Spannable) mWebTextView.getText();
                             int start = Selection.getSelectionStart(text);
                             int end = Selection.getSelectionEnd(text);
-                            mTextEntry.setInPassword(true);
+                            mWebTextView.setInPassword(true);
                             // Restore the selection, which may have been
                             // ruined by setInPassword.
-                            Spannable pword = (Spannable) mTextEntry.getText();
+                            Spannable pword =
+                                    (Spannable) mWebTextView.getText();
                             Selection.setSelection(pword, start, end);
                         // If the text entry has created more events, ignore
                         // this one.
                         } else if (msg.arg2 == mTextGeneration) {
-                            mTextEntry.setTextAndKeepSelection(
+                            mWebTextView.setTextAndKeepSelection(
                                     (String) msg.obj);
                         }
                     }
@@ -4802,7 +4715,7 @@
                         break;
                     }
 // Do not reset the focus or clear the text; the user may have already
-// navigated or entered text at this point. The focus should have gotten 
+// navigated or entered text at this point. The focus should have gotten
 // reset, if need be, when the focus cache was built. Similarly, the text
 // view should already be torn down and rebuilt if needed.
 //                    nativeResetFocus();
@@ -4858,22 +4771,13 @@
                 case MARK_NODE_INVALID_ID:
                     nativeMarkNodeInvalid(msg.arg1);
                     break;
-                case NOTIFY_FOCUS_SET_MSG_ID:
-                    if (mNativeClass != 0) {
-                        nativeNotifyFocusSet(inEditingMode());
-                    }
-                    break;
                 case UPDATE_TEXT_ENTRY_MSG_ID:
-                    // this is sent after finishing resize in WebViewCore. Make 
+                    // this is sent after finishing resize in WebViewCore. Make
                     // sure the text edit box is still on the  screen.
-                    boolean alreadyThere = inEditingMode();
-                    if (alreadyThere && nativeUpdateFocusNode()) {
-                        FocusNode node = mFocusNode;
-                        if (node.mIsTextField || node.mIsTextArea) {
-                            mTextEntry.bringIntoView();
-                        }
+                    if (inEditingMode() && nativeCursorIsTextInput()) {
+                        mWebTextView.bringIntoView();
                     }
-                    updateTextEntry();
+                    rebuildWebTextView();
                     break;
                 case RECOMPUTE_FOCUS_MSG_ID:
                     if (mNativeClass != 0) {
@@ -4891,17 +4795,17 @@
                     }
                     break;
                 }
-                case UPDATE_TEXT_ENTRY_ADAPTER:
+                case REQUEST_FORM_DATA:
                     HashMap data = (HashMap) msg.obj;
-                    if (mTextEntry.isSameTextField(msg.arg1)) {
+                    if (mWebTextView.isSameTextField(msg.arg1)) {
                         AutoCompleteAdapter adapter =
                                 (AutoCompleteAdapter) data.get("adapter");
-                        mTextEntry.setAdapterCustom(adapter);
+                        mWebTextView.setAdapterCustom(adapter);
                     }
                     break;
                 case UPDATE_CLIPBOARD:
                     String str = (String) msg.obj;
-                    if (LOGV_ENABLED) {
+                    if (DebugFlags.WEB_VIEW) {
                         Log.v(LOGTAG, "UPDATE_CLIPBOARD " + str);
                     }
                     try {
@@ -4916,12 +4820,12 @@
                     WebViewCore.resumeUpdate(mWebViewCore);
                     break;
 
-                case LONG_PRESS_ENTER:
+                case LONG_PRESS_CENTER:
                     // as this is shared by keydown and trackballdown, reset all
                     // the states
-                    mGotEnterDown = false;
+                    mGotCenterDown = false;
                     mTrackballDown = false;
-                    // LONG_PRESS_ENTER is sent as a delayed message. If we
+                    // LONG_PRESS_CENTER is sent as a delayed message. If we
                     // switch to windows overview, the WebView will be
                     // temporarily removed from the view system. In that case,
                     // do nothing.
@@ -4957,7 +4861,7 @@
         // Passed in to a list with multiple selection to tell
         // which items are selected.
         private int[]       mSelectedArray;
-        // Passed in to a list with single selection to tell 
+        // Passed in to a list with single selection to tell
         // where the initial selection is.
         private int         mSelection;
 
@@ -4976,14 +4880,14 @@
         }
 
         /**
-         *  Subclass ArrayAdapter so we can disable OptionGroupLabels, 
+         *  Subclass ArrayAdapter so we can disable OptionGroupLabels,
          *  and allow filtering.
          */
         private class MyArrayListAdapter extends ArrayAdapter<Container> {
             public MyArrayListAdapter(Context context, Container[] objects, boolean multiple) {
-                super(context, 
+                super(context,
                             multiple ? com.android.internal.R.layout.select_dialog_multichoice :
-                            com.android.internal.R.layout.select_dialog_singlechoice, 
+                            com.android.internal.R.layout.select_dialog_singlechoice,
                             objects);
             }
 
@@ -5041,7 +4945,7 @@
             }
         }
 
-        private InvokeListBox(String[] array, boolean[] enabled, int 
+        private InvokeListBox(String[] array, boolean[] enabled, int
                 selection) {
             mSelection = selection;
             mMultiple = false;
@@ -5104,17 +5008,17 @@
         public void run() {
             final ListView listView = (ListView) LayoutInflater.from(mContext)
                     .inflate(com.android.internal.R.layout.select_dialog, null);
-            final MyArrayListAdapter adapter = new 
+            final MyArrayListAdapter adapter = new
                     MyArrayListAdapter(mContext, mContainers, mMultiple);
             AlertDialog.Builder b = new AlertDialog.Builder(mContext)
                     .setView(listView).setCancelable(true)
                     .setInverseBackgroundForced(true);
-                    
+
             if (mMultiple) {
                 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                     public void onClick(DialogInterface dialog, int which) {
                         mWebViewCore.sendMessage(
-                                EventHub.LISTBOX_CHOICES, 
+                                EventHub.LISTBOX_CHOICES,
                                 adapter.getCount(), 0,
                                 listView.getCheckedItemPositions());
                     }});
@@ -5130,10 +5034,10 @@
             listView.setFocusableInTouchMode(true);
             // There is a bug (1250103) where the checks in a ListView with
             // multiple items selected are associated with the positions, not
-            // the ids, so the items do not properly retain their checks when 
+            // the ids, so the items do not properly retain their checks when
             // filtered.  Do not allow filtering on multiple lists until
             // that bug is fixed.
-            
+
             listView.setTextFilterEnabled(!mMultiple);
             if (mMultiple) {
                 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
@@ -5196,47 +5100,26 @@
     }
 
     // called by JNI
-    private void sendFinalFocus(int frame, int node, int x, int y) {
-        WebViewCore.FocusData focusData = new WebViewCore.FocusData();
-        focusData.mFrame = frame;
-        focusData.mNode = node;
-        focusData.mX = x;
-        focusData.mY = y;
-        mWebViewCore.sendMessage(EventHub.SET_FINAL_FOCUS, 
-                EventHub.NO_FOCUS_CHANGE_BLOCK, 0, focusData);
+    private void sendMoveMouse(int frame, int node, int x, int y) {
+        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
+                new WebViewCore.CursorData(frame, node, x, y));
     }
 
     // called by JNI
-    private void setFocusData(int moveGeneration, int buildGeneration,
-            int frame, int node, int x, int y, boolean ignoreNullFocus) {
-        mFocusData.mMoveGeneration = moveGeneration;
-        mFocusData.mBuildGeneration = buildGeneration;
-        mFocusData.mFrame = frame;
-        mFocusData.mNode = node;
-        mFocusData.mX = x;
-        mFocusData.mY = y;
-        mFocusData.mIgnoreNullFocus = ignoreNullFocus;
-    }
-    
-    // called by JNI
-    private void sendKitFocus() {
-        WebViewCore.FocusData focusData = new WebViewCore.FocusData(mFocusData);
-        mWebViewCore.sendMessage(EventHub.SET_KIT_FOCUS, focusData);
+    private void sendMoveMouseIfLatest() {
+        mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST, cursorData());
     }
 
     // called by JNI
-    private void sendMotionUp(int touchGeneration, int buildGeneration,
-            int frame, int node, int x, int y, int size,
-            boolean retry) {
+    private void sendMotionUp(int touchGeneration,
+            int frame, int node, int x, int y, int size) {
         WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
         touchUpData.mMoveGeneration = touchGeneration;
-        touchUpData.mBuildGeneration = buildGeneration;
         touchUpData.mSize = size;
-        touchUpData.mRetry = retry;
-        mFocusData.mFrame = touchUpData.mFrame = frame;
-        mFocusData.mNode = touchUpData.mNode = node;
-        mFocusData.mX = touchUpData.mX = x;
-        mFocusData.mY = touchUpData.mY = y;
+        touchUpData.mFrame = frame;
+        touchUpData.mNode = node;
+        touchUpData.mX = x;
+        touchUpData.mY = y;
         mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
     }
 
@@ -5275,7 +5158,7 @@
     private void viewInvalidate() {
         invalidate();
     }
-    
+
     // return true if the key was handled
     private boolean navHandledKey(int keyCode, int count, boolean noScroll
             , long time) {
@@ -5283,9 +5166,9 @@
             return false;
         }
         mLastFocusTime = time;
-        mLastFocusBounds = nativeGetFocusRingBounds();
+        mLastFocusBounds = nativeGetCursorRingBounds();
         boolean keyHandled = nativeMoveFocus(keyCode, count, noScroll) == false;
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "navHandledKey mLastFocusBounds=" + mLastFocusBounds
                     + " mLastFocusTime=" + mLastFocusTime
                     + " handled=" + keyHandled);
@@ -5293,7 +5176,7 @@
         if (keyHandled == false || mHeightCanMeasure == false) {
             return keyHandled;
         }
-        Rect contentFocus = nativeGetFocusRingBounds();
+        Rect contentFocus = nativeGetCursorRingBounds();
         if (contentFocus.isEmpty()) return keyHandled;
         Rect viewFocus = contentToView(contentFocus);
         Rect visRect = new Rect();
@@ -5317,14 +5200,14 @@
         }
         if (mLastFocusBounds.isEmpty()) return keyHandled;
         if (mLastFocusBounds.equals(contentFocus)) return keyHandled;
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "navHandledKey contentFocus=" + contentFocus);
         }
         requestRectangleOnScreen(viewFocus);
         mUserScroll = true;
         return keyHandled;
     }
-    
+
     /**
      * Set the background color. It's white by default. Pass
      * zero to make the view transparent.
@@ -5339,7 +5222,7 @@
         nativeDebugDump();
         mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
     }
-    
+
     /**
      *  Update our cache with updatedText.
      *  @param updatedText  The new text to put in our cache.
@@ -5349,52 +5232,70 @@
         // we recognize that it is up to date.
         nativeUpdateCachedTextfield(updatedText, mTextGeneration);
     }
-    
-    // Never call this version except by updateCachedTextfield(String) -
-    // we always want to pass in our generation number.
-    private native void     nativeUpdateCachedTextfield(String updatedText, 
-            int generation);
+
     private native void     nativeClearFocus(int x, int y);
     private native void     nativeCreate(int ptr);
+    private native int      nativeCursorFramePointer();
+    private native Rect     nativeCursorNodeBounds();
+    /* package */ native int nativeCursorNodePointer();
+    /* package */ native boolean nativeCursorMatchesFocus();
+    private native boolean  nativeCursorIntersects(Rect visibleRect);
+    private native boolean  nativeCursorIsAnchor();
+    private native boolean  nativeCursorIsTextInput();
+    private native String   nativeCursorText();
+    private native boolean  nativeCursorWantsKeyEvents();
     private native void     nativeDebugDump();
     private native void     nativeDestroy();
-    private native void     nativeDrawFocusRing(Canvas content);
+    private native void     nativeDrawCursorRing(Canvas content);
+    private native void     nativeDrawMatches(Canvas canvas);
     private native void     nativeDrawSelection(Canvas content
             , int x, int y, boolean extendSelection);
     private native void     nativeDrawSelectionRegion(Canvas content);
-    private native boolean  nativeUpdateFocusNode();
-    private native Rect     nativeGetFocusRingBounds();
-    private native Rect     nativeGetNavBounds();
+    private native void     nativeDumpDisplayTree(String urlOrNull);
+    private native int      nativeFindAll(String findLower, String findUpper);
+    private native void     nativeFindNext(boolean forward);
+    private native boolean  nativeFocusIsPassword();
+    private native boolean  nativeFocusIsRtlText();
+    private native boolean  nativeFocusIsTextField();
+    private native boolean  nativeFocusIsTextInput();
+    private native int      nativeFocusMaxLength();
+    private native String   nativeFocusName();
+    private native Rect     nativeFocusNodeBounds();
+    /* package */ native int nativeFocusNodePointer();
+    private native String   nativeFocusText();
+    private native int      nativeFocusTextSize();
+    /**
+     * Returns true if the native focus nodes says it wants to handle key events
+     * (ala plugins). This can only be called if mNativeClass is non-zero!
+     */
+    private native Rect     nativeGetCursorRingBounds();
+    private native Region   nativeGetSelection();
+    private native boolean  nativeHasCursorNode();
+    private native boolean  nativeHasFocusNode();
+    private native String   nativeImageURI(int x, int y);
     private native void     nativeInstrumentReport();
     private native void     nativeMarkNodeInvalid(int node);
     // return true if the page has been scrolled
     private native boolean  nativeMotionUp(int x, int y, int slop);
     // returns false if it handled the key
-    private native boolean  nativeMoveFocus(int keyCode, int count, 
+    private native boolean  nativeMoveFocus(int keyCode, int count,
             boolean noScroll);
-    private native void     nativeNotifyFocusSet(boolean inEditingMode);
+    private native int      nativeMoveGeneration();
+    private native void     nativeMoveSelection(int x, int y,
+            boolean extendSelection);
     private native void     nativeRecomputeFocus();
     // Like many other of our native methods, you must make sure that
     // mNativeClass is not null before calling this method.
     private native void     nativeRecordButtons(boolean focused,
             boolean pressed, boolean invalidate);
-    private native void     nativeResetFocus();
-    private native void     nativeResetNavClipBounds();
     private native void     nativeSelectBestAt(Rect rect);
     private native void     nativeSetFindIsDown();
     private native void     nativeSetFollowedLink(boolean followed);
     private native void     nativeSetHeightCanMeasure(boolean measure);
-    private native void     nativeSetNavBounds(Rect rect);
-    private native void     nativeSetNavClipBounds(Rect rect);
-    private native String   nativeImageURI(int x, int y);
-    /**
-     * Returns true if the native focus nodes says it wants to handle key events
-     * (ala plugins). This can only be called if mNativeClass is non-zero!
-     */
-    private native boolean  nativeFocusNodeWantsKeyEvents();
-    private native void     nativeMoveSelection(int x, int y
-            , boolean extendSelection);
-    private native Region   nativeGetSelection();
+    private native int      nativeTextGeneration();
+    // Never call this version except by updateCachedTextfield(String) -
+    // we always want to pass in our generation number.
+    private native void     nativeUpdateCachedTextfield(String updatedText,
+            int generation);
 
-    private native void nativeDumpDisplayTree(String urlOrNull);
 }
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index f9bbc31..f4b99b9 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -41,8 +41,6 @@
 final class WebViewCore {
 
     private static final String LOGTAG = "webcore";
-    static final boolean DEBUG = false;
-    static final boolean LOGV_ENABLED = DEBUG;
 
     static {
         // Load libwebcore during static initialization. This happens in the
@@ -96,7 +94,7 @@
     private int mViewportMaximumScale = 0;
 
     private boolean mViewportUserScalable = true;
-    
+
     private int mRestoredScale = 100;
     private int mRestoredX = 0;
     private int mRestoredY = 0;
@@ -229,6 +227,16 @@
     }
 
     /**
+     * Add an error message to the client's console.
+     * @param message The message to add
+     * @param lineNumber the line on which the error occurred
+     * @param sourceID the filename of the source that caused the error.
+     */
+    protected void addMessageToConsole(String message, int lineNumber, String sourceID) {
+        mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID);
+    }
+
+    /**
      * Invoke a javascript alert.
      * @param message The message displayed in the alert.
      */
@@ -308,15 +316,15 @@
      * Empty the picture set.
      */
     private native void nativeClearContent();
-    
+
     /**
      * Create a flat picture from the set of pictures.
      */
     private native void nativeCopyContentToPicture(Picture picture);
-   
+
     /**
      * Draw the picture set with a background color. Returns true
-     * if some individual picture took too long to draw and can be 
+     * if some individual picture took too long to draw and can be
      * split into parts. Called from the UI thread.
      */
     private native boolean nativeDrawContent(Canvas canvas, int color);
@@ -325,13 +333,13 @@
      * check to see if picture is blank and in progress
      */
     private native boolean nativePictureReady();
-    
+
     /**
      * Redraw a portion of the picture set. The Point wh returns the
      * width and height of the overall picture.
      */
     private native boolean nativeRecordContent(Region invalRegion, Point wh);
-    
+
     /**
      * Splits slow parts of the picture set. Called from the webkit
      * thread after nativeDrawContent returns true.
@@ -359,13 +367,12 @@
             float scale, int realScreenWidth, int screenHeight);
 
     private native int nativeGetContentMinPrefWidth();
-    
-    // Start: functions that deal with text editing
-    private native void nativeReplaceTextfieldText(int frame, int node, int x, 
-            int y, int oldStart, int oldEnd, String replace, int newStart, 
-            int newEnd);
 
-    private native void passToJs(int frame, int node, int x, int y, int gen,
+    // Start: functions that deal with text editing
+    private native void nativeReplaceTextfieldText(
+            int oldStart, int oldEnd, String replace, int newStart, int newEnd);
+
+    private native void passToJs(int gen,
             String currentText, int keyCode, int keyValue, boolean down,
             boolean cap, boolean fn, boolean sym);
 
@@ -373,31 +380,31 @@
 
     private native void nativeSaveDocumentState(int frame);
 
-    private native void nativeSetFinalFocus(int framePtr, int nodePtr, int x,
-            int y, boolean block);
+    private native void nativeMoveMouse(int framePtr, int nodePtr, int x,
+            int y);
 
-    private native void nativeSetKitFocus(int moveGeneration,
-            int buildGeneration, int framePtr, int nodePtr, int x, int y,
+    private native void nativeMoveMouseIfLatest(int moveGeneration,
+            int framePtr, int nodePtr, int x, int y,
             boolean ignoreNullFocus);
 
     private native String nativeRetrieveHref(int framePtr, int nodePtr);
-    
-    private native void nativeTouchUp(int touchGeneration, 
-            int buildGeneration, int framePtr, int nodePtr, int x, int y, 
-            int size, boolean retry);
+
+    private native void nativeTouchUp(int touchGeneration,
+            int framePtr, int nodePtr, int x, int y,
+            int size);
 
     private native boolean nativeHandleTouchEvent(int action, int x, int y);
 
     private native void nativeUnblockFocus();
-    
+
     private native void nativeUpdateFrameCache();
-    
+
     private native void nativeSetSnapAnchor(int x, int y);
-    
+
     private native void nativeSnapToAnchor();
-    
+
     private native void nativeSetBackgroundColor(int color);
-    
+
     private native void nativeDumpDomTree(boolean useFile);
 
     private native void nativeDumpRenderTree(boolean useFile);
@@ -406,13 +413,12 @@
 
     /**
      *  Delete text from start to end in the focused textfield. If there is no
-     *  focus, or if start == end, silently fail.  If start and end are out of 
+     *  focus, or if start == end, silently fail.  If start and end are out of
      *  order, swap them.
      *  @param  start   Beginning of selection to delete.
      *  @param  end     End of selection to delete.
      */
-    private native void nativeDeleteSelection(int frame, int node, int x, int y,
-        int start, int end);
+    private native void nativeDeleteSelection(int start, int end);
 
     /**
      *  Set the selection to (start, end) in the focused textfield. If start and
@@ -420,11 +426,10 @@
      *  @param  start   Beginning of selection.
      *  @param  end     End of selection.
      */
-    private native void nativeSetSelection(int frame, int node, int x, int y,
-        int start, int end);
+    private native void nativeSetSelection(int start, int end);
 
     private native String nativeGetSelection(Region sel);
-    
+
     // Register a scheme to be treated as local scheme so that it can access
     // local asset files for resources
     private native void nativeRegisterURLSchemeAsLocal(String scheme);
@@ -485,7 +490,7 @@
                                     CacheManager.endCacheTransaction();
                                     CacheManager.startCacheTransaction();
                                     sendMessageDelayed(
-                                            obtainMessage(CACHE_TICKER), 
+                                            obtainMessage(CACHE_TICKER),
                                             CACHE_TICKER_INTERVAL);
                                 }
                                 break;
@@ -510,19 +515,15 @@
         }
     }
 
-    static class FocusData {
-        FocusData() {}
-        FocusData(FocusData d) {
-            mMoveGeneration = d.mMoveGeneration;
-            mBuildGeneration = d.mBuildGeneration;
-            mFrame = d.mFrame;
-            mNode = d.mNode;
-            mX = d.mX;
-            mY = d.mY;
-            mIgnoreNullFocus = d.mIgnoreNullFocus;
+    static class CursorData {
+        CursorData() {}
+        CursorData(int frame, int node, int x, int y) {
+            mFrame = frame;
+            mNode = node;
+            mX = x;
+            mY = y;
         }
         int mMoveGeneration;
-        int mBuildGeneration;
         int mFrame;
         int mNode;
         int mX;
@@ -532,13 +533,11 @@
 
     static class TouchUpData {
         int mMoveGeneration;
-        int mBuildGeneration;
         int mFrame;
         int mNode;
         int mX;
         int mY;
         int mSize;
-        boolean mRetry;
     }
 
     static class TouchEventData {
@@ -583,8 +582,8 @@
             "POST_URL", // = 132;
             "SPLIT_PICTURE_SET", // = 133;
             "CLEAR_CONTENT", // = 134;
-            "SET_FINAL_FOCUS", // = 135;
-            "SET_KIT_FOCUS", // = 136;
+            "SET_MOVE_MOUSE", // = 135;
+            "SET_MOVE_MOUSE_IF_LATEST", // = 136;
             "REQUEST_FOCUS_HREF", // = 137;
             "ADD_JS_INTERFACE", // = 138;
             "LOAD_DATA", // = 139;
@@ -632,10 +631,10 @@
         static final int POST_URL = 132;
         static final int SPLIT_PICTURE_SET = 133;
         static final int CLEAR_CONTENT = 134;
-        
+
         // UI nav messages
-        static final int SET_FINAL_FOCUS = 135;
-        static final int SET_KIT_FOCUS = 136;
+        static final int SET_MOVE_MOUSE = 135;
+        static final int SET_MOVE_MOUSE_IF_LATEST = 136;
         static final int REQUEST_FOCUS_HREF = 137;
         static final int ADD_JS_INTERFACE = 138;
         static final int LOAD_DATA = 139;
@@ -668,10 +667,6 @@
 
         // private message ids
         private static final int DESTROY =     200;
-        
-        // flag values passed to message SET_FINAL_FOCUS
-        static final int NO_FOCUS_CHANGE_BLOCK = 0;
-        static final int BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP = 1;
 
         // Private handler for WebCore messages.
         private Handler mHandler;
@@ -700,8 +695,8 @@
             mHandler = new Handler() {
                 @Override
                 public void handleMessage(Message msg) {
-                    if (LOGV_ENABLED) {
-                        Log.v(LOGTAG, msg.what < LOAD_URL || msg.what 
+                    if (DebugFlags.WEB_VIEW_CORE) {
+                        Log.v(LOGTAG, msg.what < LOAD_URL || msg.what
                                 > SET_ACTIVE ? Integer.toString(msg.what)
                                 : HandlerDebugString[msg.what - LOAD_URL]);
                     }
@@ -744,7 +739,7 @@
                                      * we automatically add the scheme of the
                                      * baseUrl for local access as long as it is
                                      * not http(s)/ftp(s)/about/javascript
-                                     */ 
+                                     */
                                     String scheme = baseUrl.substring(0, i);
                                     if (!scheme.startsWith("http") &&
                                             !scheme.startsWith("ftp") &&
@@ -762,9 +757,9 @@
                             break;
 
                         case STOP_LOADING:
-                            // If the WebCore has committed the load, but not 
-                            // finished the first layout yet, we need to set 
-                            // first layout done to trigger the interpreted side sync 
+                            // If the WebCore has committed the load, but not
+                            // finished the first layout yet, we need to set
+                            // first layout done to trigger the interpreted side sync
                             // up with native side
                             if (mBrowserFrame.committed()
                                     && !mBrowserFrame.firstLayoutDone()) {
@@ -800,7 +795,7 @@
                             // (inv-zoom)
                             nativeSetScrollOffset(msg.arg1, msg.arg2);
                             break;
-                            
+
                         case SET_GLOBAL_BOUNDS:
                             Rect r = (Rect) msg.obj;
                             nativeSetGlobalBounds(r.left, r.top, r.width(),
@@ -811,7 +806,7 @@
                             // If it is a standard load and the load is not
                             // committed yet, we interpret BACK as RELOAD
                             if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
-                                    (mBrowserFrame.loadType() == 
+                                    (mBrowserFrame.loadType() ==
                                     BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
                                 mBrowserFrame.reload(true);
                             } else {
@@ -877,28 +872,24 @@
                                     close(mBrowserFrame.mNativeFrame);
                             break;
 
-                        case REPLACE_TEXT: 
+                        case REPLACE_TEXT:
                             HashMap jMap = (HashMap) msg.obj;
-                            FocusData fData = (FocusData) jMap.get("focusData");
                             String replace = (String) jMap.get("replace");
-                            int newStart = 
+                            int newStart =
                                     ((Integer) jMap.get("start")).intValue();
-                            int newEnd = 
+                            int newEnd =
                                     ((Integer) jMap.get("end")).intValue();
-                            nativeReplaceTextfieldText(fData.mFrame,
-                                    fData.mNode, fData.mX, fData.mY, msg.arg1,
+                            nativeReplaceTextfieldText(msg.arg1,
                                     msg.arg2, replace, newStart, newEnd);
                             break;
 
                         case PASS_TO_JS: {
                             HashMap jsMap = (HashMap) msg.obj;
-                            FocusData fDat = (FocusData) jsMap.get("focusData");
                             KeyEvent evt = (KeyEvent) jsMap.get("event");
                             int keyCode = evt.getKeyCode();
                             int keyValue = evt.getUnicodeChar();
                             int generation = msg.arg1;
-                            passToJs(fDat.mFrame, fDat.mNode, fDat.mX, fDat.mY,
-                                    generation,
+                            passToJs(generation,
                                     (String) jsMap.get("currentText"),
                                     keyCode,
                                     keyValue,
@@ -909,7 +900,7 @@
                         }
 
                         case SAVE_DOCUMENT_STATE: {
-                            FocusData fDat = (FocusData) msg.obj;
+                            CursorData fDat = (CursorData) msg.obj;
                             nativeSaveDocumentState(fDat.mFrame);
                             break;
                         }
@@ -922,11 +913,9 @@
                         case TOUCH_UP:
                             TouchUpData touchUpData = (TouchUpData) msg.obj;
                             nativeTouchUp(touchUpData.mMoveGeneration,
-                                    touchUpData.mBuildGeneration,
                                     touchUpData.mFrame, touchUpData.mNode,
-                                    touchUpData.mX, touchUpData.mY, 
-                                    touchUpData.mSize,
-                                    touchUpData.mRetry);
+                                    touchUpData.mX, touchUpData.mY,
+                                    touchUpData.mSize);
                             break;
 
                         case TOUCH_EVENT: {
@@ -961,22 +950,20 @@
                             mBrowserFrame.documentAsText((Message) msg.obj);
                             break;
 
-                        case SET_FINAL_FOCUS:
-                            FocusData finalData = (FocusData) msg.obj;
-                            nativeSetFinalFocus(finalData.mFrame,
-                                     finalData.mNode, finalData.mX, 
-                                     finalData.mY, msg.arg1 
-                                     != EventHub.NO_FOCUS_CHANGE_BLOCK);
+                        case SET_MOVE_MOUSE:
+                            CursorData finalData = (CursorData) msg.obj;
+                            nativeMoveMouse(finalData.mFrame,
+                                     finalData.mNode, finalData.mX,
+                                     finalData.mY);
                             break;
 
                         case UNBLOCK_FOCUS:
                             nativeUnblockFocus();
                             break;
 
-                        case SET_KIT_FOCUS:
-                            FocusData focusData = (FocusData) msg.obj;
-                            nativeSetKitFocus(focusData.mMoveGeneration,
-                                    focusData.mBuildGeneration,
+                        case SET_MOVE_MOUSE_IF_LATEST:
+                            CursorData focusData = (CursorData) msg.obj;
+                            nativeMoveMouseIfLatest(focusData.mMoveGeneration,
                                     focusData.mFrame, focusData.mNode,
                                     focusData.mX, focusData.mY,
                                     focusData.mIgnoreNullFocus);
@@ -989,7 +976,7 @@
                             hrefMsg.sendToTarget();
                             break;
                         }
-                            
+
                         case UPDATE_CACHE_AND_TEXT_ENTRY:
                             nativeUpdateFrameCache();
                             // FIXME: this should provide a minimal rectangle
@@ -1009,21 +996,15 @@
                         case SET_SNAP_ANCHOR:
                             nativeSetSnapAnchor(msg.arg1, msg.arg2);
                             break;
-                            
+
                         case DELETE_SELECTION:
-                            FocusData delData = (FocusData) msg.obj;
-                            nativeDeleteSelection(delData.mFrame,
-                                     delData.mNode, delData.mX, 
-                                     delData.mY, msg.arg1, msg.arg2);
+                            nativeDeleteSelection(msg.arg1, msg.arg2);
                             break;
 
                         case SET_SELECTION:
-                            FocusData selData = (FocusData) msg.obj;
-                            nativeSetSelection(selData.mFrame,
-                                     selData.mNode, selData.mX, 
-                                     selData.mY, msg.arg1, msg.arg2);
+                            nativeSetSelection(msg.arg1, msg.arg2);
                             break;
-                            
+
                         case LISTBOX_CHOICES:
                             SparseBooleanArray choices = (SparseBooleanArray)
                                     msg.obj;
@@ -1032,18 +1013,18 @@
                             for (int c = 0; c < choicesSize; c++) {
                                 choicesArray[c] = choices.get(c);
                             }
-                            nativeSendListBoxChoices(choicesArray, 
+                            nativeSendListBoxChoices(choicesArray,
                                     choicesSize);
                             break;
 
                         case SINGLE_LISTBOX_CHOICE:
                             nativeSendListBoxChoice(msg.arg1);
                             break;
-                            
+
                         case SET_BACKGROUND_COLOR:
                             nativeSetBackgroundColor(msg.arg1);
                             break;
-                            
+
                         case GET_SELECTION:
                             String str = nativeGetSelection((Region) msg.obj);
                             Message.obtain(mWebView.mPrivateHandler
@@ -1072,7 +1053,7 @@
                             nativeSplitContent();
                             mSplitPictureIsScheduled = false;
                             break;
-                            
+
                         case CLEAR_CONTENT:
                             // Clear the view so that onDraw() will draw nothing
                             // but white background
@@ -1174,7 +1155,7 @@
     //-------------------------------------------------------------------------
 
     void stopLoading() {
-        if (LOGV_ENABLED) Log.v(LOGTAG, "CORE stopLoading");
+        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
         if (mBrowserFrame != null) {
             mBrowserFrame.stopLoading();
         }
@@ -1250,19 +1231,22 @@
     //-------------------------------------------------------------------------
 
     private void loadUrl(String url) {
-        if (LOGV_ENABLED) Log.v(LOGTAG, " CORE loadUrl " + url);
+        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
         mBrowserFrame.loadUrl(url);
     }
 
     private void key(KeyEvent evt, boolean isDown) {
-        if (LOGV_ENABLED) {
+        if (DebugFlags.WEB_VIEW_CORE) {
             Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
                     + evt);
         }
-        if (!nativeKey(evt.getKeyCode(), evt.getUnicodeChar(),
+        int keyCode = evt.getKeyCode();
+        if (!nativeKey(keyCode, evt.getUnicodeChar(),
                 evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
-                isDown)) {
+                isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
             // bubble up the event handling
+            // but do not bubble up the ENTER key, which would open the search
+            // bar without any text.
             mCallbackProxy.onUnhandledKeyEvent(evt);
         }
     }
@@ -1273,7 +1257,7 @@
 
     // notify webkit that our virtual view size changed size (after inv-zoom)
     private void viewSizeChanged(int w, int h, float scale) {
-        if (LOGV_ENABLED) Log.v(LOGTAG, "CORE onSizeChanged");
+        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE onSizeChanged");
         if (w == 0) {
             Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
             return;
@@ -1282,7 +1266,7 @@
                 && (w < mViewportWidth || mViewportWidth == -1)) {
             int width = mViewportWidth;
             if (mViewportWidth == -1) {
-                if (mSettings.getLayoutAlgorithm() == 
+                if (mSettings.getLayoutAlgorithm() ==
                         WebSettings.LayoutAlgorithm.NORMAL) {
                     width = WebView.ZOOM_OUT_WIDTH;
                 } else {
@@ -1313,7 +1297,7 @@
         if (needInvalidate) {
             // ensure {@link #webkitDraw} is called as we were blocking in
             // {@link #contentDraw} when mCurrentViewWidth is 0
-            if (LOGV_ENABLED) Log.v(LOGTAG, "viewSizeChanged");
+            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
             contentDraw();
         }
         mEventHub.sendMessage(Message.obtain(null,
@@ -1329,7 +1313,7 @@
 
     // Used to avoid posting more than one draw message.
     private boolean mDrawIsScheduled;
-    
+
     // Used to avoid posting more than one split picture message.
     private boolean mSplitPictureIsScheduled;
 
@@ -1338,7 +1322,7 @@
 
     // Used to end scale+scroll mode, accessed by both threads
     boolean mEndScaleZoom = false;
-    
+
     public class DrawData {
         public DrawData() {
             mInvalRegion = new Region();
@@ -1348,21 +1332,21 @@
         public Point mViewPoint;
         public Point mWidthHeight;
     }
-    
+
     private void webkitDraw() {
         mDrawIsScheduled = false;
         DrawData draw = new DrawData();
-        if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw start");
-        if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight) 
+        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
+        if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
                 == false) {
-            if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw abort");
+            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
             return;
         }
         if (mWebView != null) {
             // Send the native view size that was used during the most recent
             // layout.
             draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
-            if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
+            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
             Message.obtain(mWebView.mPrivateHandler,
                     WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
             if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
@@ -1427,9 +1411,9 @@
         sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
                 .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
         // Note: there is one possible failure mode. If pauseUpdate() is called
-        // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out 
-        // of the queue and about to be executed. mDrawIsScheduled may be set to 
-        // false in webkitDraw(). So update won't be blocked. But at least the 
+        // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out
+        // of the queue and about to be executed. mDrawIsScheduled may be set to
+        // false in webkitDraw(). So update won't be blocked. But at least the
         // webcore thread priority is still lowered.
         if (core != null) {
             synchronized (core) {
@@ -1449,7 +1433,7 @@
             synchronized (core) {
                 core.mDrawIsScheduled = false;
                 core.mDrawIsPaused = false;
-                if (LOGV_ENABLED) Log.v(LOGTAG, "resumeUpdate");
+                if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "resumeUpdate");
                 core.contentDraw();
             }
         }
@@ -1498,7 +1482,7 @@
             mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
         }
     }
-    
+
     // called by JNI
     private void contentScrollBy(int dx, int dy, boolean animate) {
         if (!mBrowserFrame.firstLayoutDone()) {
@@ -1574,14 +1558,6 @@
     }
 
     // called by JNI
-    private void sendNotifyFocusSet() {
-        if (mWebView != null) {
-            Message.obtain(mWebView.mPrivateHandler,
-                    WebView.NOTIFY_FOCUS_SET_MSG_ID).sendToTarget();
-        }
-    }
-
-    // called by JNI
     private void sendNotifyProgressFinished() {
         sendUpdateTextEntry();
         // as CacheManager can behave based on database transaction, we need to
@@ -1617,12 +1593,12 @@
     }
 
     private native void setViewportSettingsFromNative();
-    
+
     // called by JNI
     private void didFirstLayout(boolean standardLoad) {
         // Trick to ensure that the Picture has the exact height for the content
         // by forcing to layout with 0 height after the page is ready, which is
-        // indicated by didFirstLayout. This is essential to get rid of the 
+        // indicated by didFirstLayout. This is essential to get rid of the
         // white space in the GMail which uses WebView for message view.
         if (mWebView != null && mWebView.mHeightCanMeasure) {
             mWebView.mLastHeightSent = 0;
@@ -1667,7 +1643,7 @@
                 mViewportMaximumScale = mViewportInitialScale;
             } else if (mViewportInitialScale == 0) {
                 mViewportInitialScale = mViewportMaximumScale;
-            }            
+            }
         }
         if (mViewportWidth < 0 && mViewportInitialScale == 100) {
             mViewportWidth = 0;
@@ -1737,7 +1713,7 @@
             String text, int textGeneration) {
         if (mWebView != null) {
             Message msg = Message.obtain(mWebView.mPrivateHandler,
-                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr, 
+                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
                     textGeneration, text);
             msg.getData().putBoolean("password", changeToPassword);
             msg.sendToTarget();
@@ -1763,7 +1739,7 @@
         if (mWebView != null) {
             mWebView.requestListBox(array, enabledArray, selection);
         }
-        
+
     }
 
     private native void nativePause();
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index ec02143..14a85f8 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1056,11 +1056,12 @@
     public Parcelable onSaveInstanceState() {
         /*
          * This doesn't really make sense as the place to dismiss the
-         * popup, but there don't seem to be any other useful hooks
+         * popups, but there don't seem to be any other useful hooks
          * that happen early enough to keep from getting complaints
          * about having leaked the window.
          */
         dismissPopup();
+        dismissGesturesPopup();
 
         Parcelable superState = super.onSaveInstanceState();
 
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 99cf6f8c..a195ac7 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1852,18 +1852,25 @@
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
         boolean populated = super.dispatchPopulateAccessibilityEvent(event);
 
+        // If the item count is less than 15 then subtract disabled items from the count and
+        // position. Otherwise ignore disabled items.
         if (!populated) {
             int itemCount = 0;
             int currentItemIndex = getSelectedItemPosition();
 
             ListAdapter adapter = getAdapter();
             if (adapter != null) {
-                for (int i = 0, count = adapter.getCount(); i < count; i++) {
-                    if (adapter.isEnabled(i)) {
-                        itemCount++;
-                    } else if (i <= currentItemIndex) {
-                        currentItemIndex--;
+                final int count = adapter.getCount();
+                if (count < 15) {
+                    for (int i = 0; i < count; i++) {
+                        if (adapter.isEnabled(i)) {
+                            itemCount++;
+                        } else if (i <= currentItemIndex) {
+                            currentItemIndex--;
+                        }
                     }
+                } else {
+                    itemCount = count;
                 }
             }
 
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index ef240e0..84cf2c8 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -37,19 +37,19 @@
  * A Layout where the positions of the children can be described in relation to each other or to the
  * parent. For the sake of efficiency, the relations between views are evaluated in one pass, so if
  * view Y is dependent on the position of view X, make sure the view X comes first in the layout.
- * 
+ *
  * <p>
  * Note that you cannot have a circular dependency between the size of the RelativeLayout and the
  * position of its children. For example, you cannot have a RelativeLayout whose height is set to
  * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to
  * {@link #ALIGN_PARENT_BOTTOM}.
  * </p>
- * 
+ *
  * <p>
  * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
  * layout attributes
  * </p>
- * 
+ *
  * @attr ref android.R.styleable#RelativeLayout_gravity
  * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
  */
@@ -327,7 +327,7 @@
         }
 
         if (widthMode != MeasureSpec.EXACTLY) {
-            // Width already has left padding in it since it was calculated by looking at 
+            // Width already has left padding in it since it was calculated by looking at
             // the right of each child view
             width += mPaddingRight;
 
@@ -339,7 +339,7 @@
             width = resolveSize(width, widthMeasureSpec);
         }
         if (heightMode != MeasureSpec.EXACTLY) {
-            // Height already has top padding in it since it was calculated by looking at 
+            // Height already has top padding in it since it was calculated by looking at
             // the bottom of each child view
             height += mPaddingBottom;
 
@@ -881,7 +881,7 @@
             @ViewDebug.IntToString(from = RIGHT_OF,            to = "rightOf")
         }, mapping = {
             @ViewDebug.IntToString(from = TRUE, to = "true"),
-            @ViewDebug.IntToString(from = 0,    to = "FALSE/NO_ID")
+            @ViewDebug.IntToString(from = 0,    to = "false/NO_ID")
         })
         private int[] mRules = new int[VERB_COUNT];
 
diff --git a/core/java/com/android/internal/backup/AdbTransport.java b/core/java/com/android/internal/backup/AdbTransport.java
new file mode 100644
index 0000000..acb3273
--- /dev/null
+++ b/core/java/com/android/internal/backup/AdbTransport.java
@@ -0,0 +1,28 @@
+package com.android.internal.backup;
+
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+/**
+ * Backup transport for full backup over adb.  This transport pipes everything to
+ * a file in a known location in /cache, which 'adb backup' then pulls to the desktop
+ * (deleting it afterwards).
+ */
+
+public class AdbTransport extends IBackupTransport.Stub {
+
+    public int startSession() throws RemoteException {
+        return 0;
+    }
+
+    public int endSession() throws RemoteException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int performBackup(String packageName, ParcelFileDescriptor data)
+            throws RemoteException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+}
diff --git a/core/java/com/android/internal/backup/GoogleTransport.java b/core/java/com/android/internal/backup/GoogleTransport.java
new file mode 100644
index 0000000..85ab21e
--- /dev/null
+++ b/core/java/com/android/internal/backup/GoogleTransport.java
@@ -0,0 +1,28 @@
+package com.android.internal.backup;
+
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+/**
+ * Backup transport for saving data to Google cloud storage.
+ */
+
+public class GoogleTransport extends IBackupTransport.Stub {
+
+    public int endSession() throws RemoteException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int performBackup(String packageName, ParcelFileDescriptor data)
+            throws RemoteException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int startSession() throws RemoteException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+}
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index ce39768..2b44fe7 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -16,6 +16,7 @@
 
 package com.android.internal.backup;
 
+import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 
 /** {@hide} */
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 51f3b02..99a381c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -53,7 +53,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS' 
 
     // Current on-disk Parcel version
-    private static final int VERSION = 37;
+    private static final int VERSION = 38;
 
     private final File mFile;
     private final File mBackupFile;
@@ -1342,11 +1342,13 @@
         public Map<String, ? extends BatteryStats.Uid.Pkg> getPackageStats() {
             return mPackageStats;
         }
-        
+
+        @Override
         public int getUid() {
             return mUid;
         }
-        
+
+        @Override
         public long getTcpBytesReceived(int which) {
             if (which == STATS_LAST) {
                 return mLoadedTcpBytesReceived;
@@ -1365,7 +1367,8 @@
             return mCurrentTcpBytesReceived + (mStartedTcpBytesReceived >= 0
                     ? (NetStat.getUidRxBytes(mUid) - mStartedTcpBytesReceived) : 0);
         }
-        
+
+        @Override
         public long getTcpBytesSent(int which) {
             if (which == STATS_LAST) {
                 return mLoadedTcpBytesSent;
@@ -1754,7 +1757,8 @@
             public Timer getSensorTime() {
                 return mTimer;
             }
-            
+
+            @Override
             public int getHandle() {
                 return mHandle;
             }
@@ -1780,6 +1784,11 @@
             int mStarts;
 
             /**
+             * Amount of time the process was running in the foreground.
+             */
+            long mForegroundTime;
+
+            /**
              * The amount of user time loaded from a previous save.
              */
             long mLoadedUserTime;
@@ -1795,6 +1804,11 @@
             int mLoadedStarts;
 
             /**
+             * The amount of foreground time loaded from a previous save.
+             */
+            long mLoadedForegroundTime;
+
+            /**
              * The amount of user time loaded from the previous run.
              */
             long mLastUserTime;
@@ -1810,6 +1824,11 @@
             int mLastStarts;
 
             /**
+             * The amount of foreground time loaded from the previous run
+             */
+            long mLastForegroundTime;
+
+            /**
              * The amount of user time when last unplugged.
              */
             long mUnpluggedUserTime;
@@ -1824,6 +1843,11 @@
              */
             int mUnpluggedStarts;
 
+            /**
+             * The amount of foreground time since unplugged.
+             */
+            long mUnpluggedForegroundTime;
+
             Proc() {
                 mUnpluggables.add(this);
             }
@@ -1832,6 +1856,7 @@
                 mUnpluggedUserTime = mUserTime;
                 mUnpluggedSystemTime = mSystemTime;
                 mUnpluggedStarts = mStarts;
+                mUnpluggedForegroundTime = mForegroundTime;
             }
 
             public void plug(long batteryUptime, long batteryRealtime) {
@@ -1843,30 +1868,38 @@
                 
                 out.writeLong(mUserTime);
                 out.writeLong(mSystemTime);
+                out.writeLong(mForegroundTime);
                 out.writeInt(mStarts);
                 out.writeLong(mLoadedUserTime);
                 out.writeLong(mLoadedSystemTime);
+                out.writeLong(mLoadedForegroundTime);
                 out.writeInt(mLoadedStarts);
                 out.writeLong(mLastUserTime);
                 out.writeLong(mLastSystemTime);
+                out.writeLong(mLastForegroundTime);
                 out.writeInt(mLastStarts);
                 out.writeLong(mUnpluggedUserTime);
                 out.writeLong(mUnpluggedSystemTime);
+                out.writeLong(mUnpluggedForegroundTime);
                 out.writeInt(mUnpluggedStarts);
             }
 
             void readFromParcelLocked(Parcel in) {
                 mUserTime = in.readLong();
                 mSystemTime = in.readLong();
+                mForegroundTime = in.readLong();
                 mStarts = in.readInt();
                 mLoadedUserTime = in.readLong();
                 mLoadedSystemTime = in.readLong();
+                mLoadedForegroundTime = in.readLong();
                 mLoadedStarts = in.readInt();
                 mLastUserTime = in.readLong();
                 mLastSystemTime = in.readLong();
+                mLastForegroundTime = in.readLong();
                 mLastStarts = in.readInt();
                 mUnpluggedUserTime = in.readLong();
                 mUnpluggedSystemTime = in.readLong();
+                mUnpluggedForegroundTime = in.readLong();
                 mUnpluggedStarts = in.readInt();
             }
 
@@ -1879,6 +1912,10 @@
                 mSystemTime += stime;
             }
 
+            public void addForegroundTimeLocked(long ttime) {
+                mForegroundTime += ttime;
+            }
+
             public void incStartsLocked() {
                 mStarts++;
             }
@@ -1916,6 +1953,22 @@
             }
 
             @Override
+            public long getForegroundTime(int which) {
+                long val;
+                if (which == STATS_LAST) {
+                    val = mLastForegroundTime;
+                } else {
+                    val = mForegroundTime;
+                    if (which == STATS_CURRENT) {
+                        val -= mLoadedForegroundTime;
+                    } else if (which == STATS_UNPLUGGED) {
+                        val -= mUnpluggedForegroundTime;
+                    }
+                }
+                return val;
+            }
+
+            @Override
             public int getStarts(int which) {
                 int val;
                 if (which == STATS_LAST) {
diff --git a/core/java/com/google/android/util/GoogleWebContentHelper.java b/core/java/com/google/android/util/GoogleWebContentHelper.java
index 2911420..3cdf855 100644
--- a/core/java/com/google/android/util/GoogleWebContentHelper.java
+++ b/core/java/com/google/android/util/GoogleWebContentHelper.java
@@ -130,7 +130,14 @@
         mWebView.loadUrl(mSecureUrl);
         return this;
     }
-    
+
+    public GoogleWebContentHelper loadDataWithFailUrl(String base, String data, 
+        String mimeType, String encoding, String failUrl) {
+        ensureViews();
+        mWebView.loadDataWithBaseURL(base, data, mimeType, encoding, failUrl);
+        return this;
+    }
+
     /**
      * Helper to handle the back key. Returns true if the back key was handled, 
      * otherwise returns false.
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 9584baf..6de37f0 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -24,7 +24,7 @@
 #include <utils/Log.h>
 #include <utils/misc.h>
 #include <binder/Parcel.h>
-#include <utils/string_array.h>
+#include <utils/StringArray.h>
 #include <utils/threads.h>
 #include <cutils/properties.h>
 
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 1fd15d6..137707f 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -311,7 +311,7 @@
                                    int sampleSize) {
     SkPixelRef* pr;
     // only use ashmem for large images, since mmaps come at a price
-    if (bitmap->getSize() >= 32 * 65536) {
+    if (bitmap->getSize() >= 32 * 1024) {
         pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
     } else {
         pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
@@ -520,7 +520,10 @@
     */
     AutoFDSeek as(descriptor);
 
-    return doDecode(env, stream, padding, bitmapFactoryOptions, true);
+    /* Allow purgeable iff we own the FD, i.e., in the puregeable and
+       shareable case.
+    */
+    return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
 }
 
 /*  make a deep copy of the asset, and return it as a stream, or NULL if there
diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp
index 8fce2a2..aab1233 100644
--- a/core/jni/android_backup_BackupDataOutput.cpp
+++ b/core/jni/android_backup_BackupDataOutput.cpp
@@ -20,7 +20,7 @@
 #include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 
-#include <utils/backup_helpers.h>
+#include <utils/BackupHelpers.h>
 
 namespace android
 {
diff --git a/core/jni/android_backup_FileBackupHelper.cpp b/core/jni/android_backup_FileBackupHelper.cpp
index a05d812..a46f37b 100644
--- a/core/jni/android_backup_FileBackupHelper.cpp
+++ b/core/jni/android_backup_FileBackupHelper.cpp
@@ -20,7 +20,7 @@
 #include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 
-#include <utils/backup_helpers.h>
+#include <utils/BackupHelpers.h>
 
 namespace android
 {
diff --git a/core/jni/android_bluetooth_BluetoothSocket.cpp b/core/jni/android_bluetooth_BluetoothSocket.cpp
index dc4c1d4..9c4f7c7 100644
--- a/core/jni/android_bluetooth_BluetoothSocket.cpp
+++ b/core/jni/android_bluetooth_BluetoothSocket.cpp
@@ -31,16 +31,29 @@
 #ifdef HAVE_BLUETOOTH
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/rfcomm.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sco.h>
 #endif
 
+#define TYPE_AS_STR(t) \
+    ((t) == TYPE_RFCOMM ? "RFCOMM" : ((t) == TYPE_SCO ? "SCO" : "L2CAP"))
+
 namespace android {
 
 static jfieldID  field_mAuth;     /* read-only */
 static jfieldID  field_mEncrypt;  /* read-only */
+static jfieldID  field_mType;     /* read-only */
+static jfieldID  field_mAddress;  /* read-only */
+static jfieldID  field_mPort;     /* read-only */
 static jfieldID  field_mSocketData;
 static jmethodID method_BluetoothSocket_ctor;
 static jclass    class_BluetoothSocket;
 
+/* Keep TYPE_RFCOMM etc in sync with BluetoothSocket.java */
+static const int TYPE_RFCOMM = 1;
+static const int TYPE_SCO = 2;
+static const int TYPE_L2CAP = 3;  // TODO: Test l2cap code paths
+
 static struct asocket *get_socketData(JNIEnv *env, jobject obj) {
     struct asocket *s =
             (struct asocket *) env->GetIntField(obj, field_mSocketData);
@@ -76,9 +89,25 @@
     int lm = 0;
     jboolean auth;
     jboolean encrypt;
+    jint type;
 
-    /*TODO: do not hardcode to rfcomm */
-    fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+    type = env->GetIntField(obj, field_mType);
+
+    switch (type) {
+    case TYPE_RFCOMM:
+        fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+        break;
+    case TYPE_SCO:
+        fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+        break;
+    case TYPE_L2CAP:
+        fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+        break;
+    default:
+        jniThrowIOException(env, ENOSYS);
+        return;
+    }
+
     if (fd < 0) {
         LOGV("socket() failed, throwing");
         jniThrowIOException(env, errno);
@@ -88,8 +117,17 @@
     auth = env->GetBooleanField(obj, field_mAuth);
     encrypt = env->GetBooleanField(obj, field_mEncrypt);
 
-    lm |= auth ? RFCOMM_LM_AUTH : 0;
-    lm |= encrypt? RFCOMM_LM_ENCRYPT : 0;
+    /* kernel does not yet support LM for SCO */
+    switch (type) {
+    case TYPE_RFCOMM:
+        lm |= auth ? RFCOMM_LM_AUTH : 0;
+        lm |= encrypt? RFCOMM_LM_ENCRYPT : 0;
+        break;
+    case TYPE_L2CAP:
+        lm |= auth ? L2CAP_LM_AUTH : 0;
+        lm |= encrypt? L2CAP_LM_ENCRYPT : 0;
+        break;
+    }
 
     if (lm) {
         if (setsockopt(fd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
@@ -99,36 +137,83 @@
         }
     }
 
+    LOGV("...fd %d created (%s, lm = %x)", fd, TYPE_AS_STR(type), lm);
+
     initSocketFromFdNative(env, obj, fd);
     return;
 #endif
     jniThrowIOException(env, ENOSYS);
 }
 
-static void connectNative(JNIEnv *env, jobject obj, jstring address,
-        jint port, jint timeout) {
+static void connectNative(JNIEnv *env, jobject obj) {
 #ifdef HAVE_BLUETOOTH
     LOGV(__FUNCTION__);
 
     int ret;
-    struct sockaddr_rc addr;
+    jint type;
     const char *c_address;
+    jstring address;
+    bdaddr_t bdaddress;
+    socklen_t addr_sz;
+    struct sockaddr *addr;
     struct asocket *s = get_socketData(env, obj);
 
     if (!s)
         return;
 
-    addr.rc_family = AF_BLUETOOTH;
-    addr.rc_channel = port;
+    type = env->GetIntField(obj, field_mType);
+
+    /* parse address into bdaddress */
+    address = (jstring) env->GetObjectField(obj, field_mAddress);
     c_address = env->GetStringUTFChars(address, NULL);
-    if (get_bdaddr((const char *)c_address, &addr.rc_bdaddr)) {
+    if (get_bdaddr(c_address, &bdaddress)) {
         env->ReleaseStringUTFChars(address, c_address);
         jniThrowIOException(env, EINVAL);
         return;
     }
     env->ReleaseStringUTFChars(address, c_address);
 
-    ret = asocket_connect(s, (struct sockaddr *)&addr, sizeof(addr), timeout);
+    switch (type) {
+    case TYPE_RFCOMM:
+        struct sockaddr_rc addr_rc;
+        addr = (struct sockaddr *)&addr_rc;
+        addr_sz = sizeof(addr_rc);
+
+        memset(addr, 0, addr_sz);
+        addr_rc.rc_family = AF_BLUETOOTH;
+        addr_rc.rc_channel = env->GetIntField(obj, field_mPort);
+        memcpy(&addr_rc.rc_bdaddr, &bdaddress, sizeof(bdaddr_t));
+
+        break;
+    case TYPE_SCO:
+        struct sockaddr_sco addr_sco;
+        addr = (struct sockaddr *)&addr_sco;
+        addr_sz = sizeof(addr_sco);
+
+        memset(addr, 0, addr_sz);
+        addr_sco.sco_family = AF_BLUETOOTH;
+        memcpy(&addr_sco.sco_bdaddr, &bdaddress, sizeof(bdaddr_t));
+
+        break;
+    case TYPE_L2CAP:
+        struct sockaddr_l2 addr_l2;
+        addr = (struct sockaddr *)&addr_l2;
+        addr_sz = sizeof(addr_l2);
+
+        memset(addr, 0, addr_sz);
+        addr_l2.l2_family = AF_BLUETOOTH;
+        addr_l2.l2_psm = env->GetIntField(obj, field_mPort);
+        memcpy(&addr_l2.l2_bdaddr, &bdaddress, sizeof(bdaddr_t));
+
+        break;
+    default:
+        jniThrowIOException(env, ENOSYS);
+        return;
+    }
+
+    ret = asocket_connect(s, addr, addr_sz, -1);
+    LOGV("...connect(%d, %s) = %d (errno %d)",
+            s->fd, TYPE_AS_STR(type), ret, errno);
 
     if (ret)
         jniThrowIOException(env, errno);
@@ -138,22 +223,57 @@
     jniThrowIOException(env, ENOSYS);
 }
 
-static void bindListenNative(JNIEnv *env, jobject obj, jint port) {
+static void bindListenNative(JNIEnv *env, jobject obj) {
 #ifdef HAVE_BLUETOOTH
     LOGV(__FUNCTION__);
 
-    struct sockaddr_rc addr;
+    jint type;
+    socklen_t addr_sz;
+    struct sockaddr *addr;
+    bdaddr_t bdaddr = *BDADDR_ANY;
     struct asocket *s = get_socketData(env, obj);
 
     if (!s)
         return;
 
-    memset(&addr, 0, sizeof(struct sockaddr_rc));
-    addr.rc_family = AF_BLUETOOTH;
-    addr.rc_bdaddr = *BDADDR_ANY;
-    addr.rc_channel = port;
+    type = env->GetIntField(obj, field_mType);
 
-    if (bind(s->fd, (struct sockaddr *)&addr, sizeof(addr))) {
+    switch (type) {
+    case TYPE_RFCOMM:
+        struct sockaddr_rc addr_rc;
+        addr = (struct sockaddr *)&addr_rc;
+        addr_sz = sizeof(addr_rc);
+
+        memset(addr, 0, addr_sz);
+        addr_rc.rc_family = AF_BLUETOOTH;
+        addr_rc.rc_channel = env->GetIntField(obj, field_mPort);
+        memcpy(&addr_rc.rc_bdaddr, &bdaddr, sizeof(bdaddr_t));
+        break;
+    case TYPE_SCO:
+        struct sockaddr_sco addr_sco;
+        addr = (struct sockaddr *)&addr_sco;
+        addr_sz = sizeof(addr_sco);
+
+        memset(addr, 0, addr_sz);
+        addr_sco.sco_family = AF_BLUETOOTH;
+        memcpy(&addr_sco.sco_bdaddr, &bdaddr, sizeof(bdaddr_t));
+        break;
+    case TYPE_L2CAP:
+        struct sockaddr_l2 addr_l2;
+        addr = (struct sockaddr *)&addr_l2;
+        addr_sz = sizeof(addr_l2);
+
+        memset(addr, 0, addr_sz);
+        addr_l2.l2_family = AF_BLUETOOTH;
+        addr_l2.l2_psm = env->GetIntField(obj, field_mPort);
+        memcpy(&addr_l2.l2_bdaddr, &bdaddr, sizeof(bdaddr_t));
+        break;
+    default:
+        jniThrowIOException(env, ENOSYS);
+        return;
+    }
+
+    if (bind(s->fd, addr, addr_sz)) {
         jniThrowIOException(env, errno);
         return;
     }
@@ -173,10 +293,12 @@
     LOGV(__FUNCTION__);
 
     int fd;
-    struct sockaddr_rc addr;
-    int addrlen = sizeof(addr);
+    jint type;
+    struct sockaddr *addr;
+    socklen_t addr_sz;
     jstring addr_jstr;
     char addr_cstr[BTADDR_SIZE];
+    bdaddr_t *bdaddr;
     jboolean auth;
     jboolean encrypt;
 
@@ -185,7 +307,39 @@
     if (!s)
         return NULL;
 
-    fd = asocket_accept(s, (struct sockaddr *)&addr, &addrlen, timeout);
+    type = env->GetIntField(obj, field_mType);
+
+    switch (type) {
+    case TYPE_RFCOMM:
+        struct sockaddr_rc addr_rc;
+        addr = (struct sockaddr *)&addr_rc;
+        addr_sz = sizeof(addr_rc);
+        bdaddr = &addr_rc.rc_bdaddr;
+        memset(addr, 0, addr_sz);
+        break;
+    case TYPE_SCO:
+        struct sockaddr_sco addr_sco;
+        addr = (struct sockaddr *)&addr_sco;
+        addr_sz = sizeof(addr_sco);
+        bdaddr = &addr_sco.sco_bdaddr;
+        memset(addr, 0, addr_sz);
+        break;
+    case TYPE_L2CAP:
+        struct sockaddr_l2 addr_l2;
+        addr = (struct sockaddr *)&addr_l2;
+        addr_sz = sizeof(addr_l2);
+        bdaddr = &addr_l2.l2_bdaddr;
+        memset(addr, 0, addr_sz);
+        break;
+    default:
+        jniThrowIOException(env, ENOSYS);
+        return NULL;
+    }
+
+    fd = asocket_accept(s, addr, &addr_sz, timeout);
+
+    LOGV("...accept(%d, %s) = %d (errno %d)",
+            s->fd, TYPE_AS_STR(type), fd, errno);
 
     if (fd < 0) {
         jniThrowIOException(env, errno);
@@ -195,10 +349,12 @@
     /* Connected - return new BluetoothSocket */
     auth = env->GetBooleanField(obj, field_mAuth);
     encrypt = env->GetBooleanField(obj, field_mEncrypt);
-    get_bdaddr_as_string(&addr.rc_bdaddr, addr_cstr);
+
+    get_bdaddr_as_string(bdaddr, addr_cstr);
+
     addr_jstr = env->NewStringUTF(addr_cstr);
-    return env->NewObject(class_BluetoothSocket, method_BluetoothSocket_ctor, fd,
-            auth, encrypt, addr_jstr, -1);
+    return env->NewObject(class_BluetoothSocket, method_BluetoothSocket_ctor,
+            type, fd, auth, encrypt, addr_jstr, -1);
 
 #endif
     jniThrowIOException(env, ENOSYS);
@@ -227,44 +383,72 @@
     return -1;
 }
 
-static jint readNative(JNIEnv *env, jobject obj) {
+/** jb must not be null. offset and offset+length must be within array */
+static jint readNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset,
+        jint length) {
 #ifdef HAVE_BLUETOOTH
     LOGV(__FUNCTION__);
 
-    char buf;
+    int ret;
+    jbyte *b;
     struct asocket *s = get_socketData(env, obj);
 
     if (!s)
         return -1;
 
-    if (asocket_read(s, &buf, 1, -1) < 0) {
-        jniThrowIOException(env, errno);
+    b = env->GetByteArrayElements(jb, NULL);
+    if (b == NULL) {
+        jniThrowIOException(env, EINVAL);
         return -1;
     }
 
-    return (jint)buf;
+    ret = asocket_read(s, &b[offset], length, -1);
+    if (ret < 0) {
+        jniThrowIOException(env, errno);
+        env->ReleaseByteArrayElements(jb, b, JNI_ABORT);
+        return -1;
+    }
+
+    env->ReleaseByteArrayElements(jb, b, 0);
+    return (jint)ret;
 
 #endif
     jniThrowIOException(env, ENOSYS);
     return -1;
 }
 
-static void writeNative(JNIEnv *env, jobject obj, jint data) {
+/** jb must not be null. offset and offset+length must be within array */
+static jint writeNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset,
+        jint length) {
 #ifdef HAVE_BLUETOOTH
     LOGV(__FUNCTION__);
 
-    const char buf = (char)data;
+    int ret;
+    jbyte *b;
     struct asocket *s = get_socketData(env, obj);
 
     if (!s)
-        return;
+        return -1;
 
-    if (asocket_write(s, &buf, 1, -1) < 0)
+    b = env->GetByteArrayElements(jb, NULL);
+    if (b == NULL) {
+        jniThrowIOException(env, EINVAL);
+        return -1;
+    }
+
+    ret = asocket_write(s, &b[offset], length, -1);
+    if (ret < 0) {
         jniThrowIOException(env, errno);
+        env->ReleaseByteArrayElements(jb, b, JNI_ABORT);
+        return -1;
+    }
 
-    return;
+    env->ReleaseByteArrayElements(jb, b, JNI_ABORT);  // no need to commit
+    return (jint)ret;
+
 #endif
     jniThrowIOException(env, ENOSYS);
+    return -1;
 }
 
 static void closeNative(JNIEnv *env, jobject obj) {
@@ -276,6 +460,8 @@
         return;
 
     asocket_abort(s);
+
+    LOGV("...asocket_abort(%d) complete", s->fd);
     return;
 #endif
     jniThrowIOException(env, ENOSYS);
@@ -285,10 +471,14 @@
 #ifdef HAVE_BLUETOOTH
     LOGV(__FUNCTION__);
     struct asocket *s = get_socketData(env, obj);
+    int fd = s->fd;
+
     if (!s)
         return;
 
     asocket_destroy(s);
+
+    LOGV("...asocket_destroy(%d) complete", fd);
     return;
 #endif
     jniThrowIOException(env, ENOSYS);
@@ -297,12 +487,12 @@
 static JNINativeMethod sMethods[] = {
     {"initSocketNative", "()V",  (void*) initSocketNative},
     {"initSocketFromFdNative", "(I)V",  (void*) initSocketFromFdNative},
-    {"connectNative", "(Ljava/lang/String;II)V", (void *) connectNative},
-    {"bindListenNative", "(I)V", (void *) bindListenNative},
+    {"connectNative", "()V", (void *) connectNative},
+    {"bindListenNative", "()V", (void *) bindListenNative},
     {"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative},
     {"availableNative", "()I",    (void *) availableNative},
-    {"readNative", "()I",    (void *) readNative},
-    {"writeNative", "(I)V",    (void *) writeNative},
+    {"readNative", "([BII)I",    (void *) readNative},
+    {"writeNative", "([BII)I",    (void *) writeNative},
     {"closeNative", "()V",    (void *) closeNative},
     {"destroyNative", "()V",    (void *) destroyNative},
 };
@@ -312,10 +502,13 @@
     if (clazz == NULL)
         return -1;
     class_BluetoothSocket = (jclass) env->NewGlobalRef(clazz);
+    field_mType = env->GetFieldID(clazz, "mType", "I");
+    field_mAddress = env->GetFieldID(clazz, "mAddress", "Ljava/lang/String;");
+    field_mPort = env->GetFieldID(clazz, "mPort", "I");
     field_mAuth = env->GetFieldID(clazz, "mAuth", "Z");
     field_mEncrypt = env->GetFieldID(clazz, "mEncrypt", "Z");
     field_mSocketData = env->GetFieldID(clazz, "mSocketData", "I");
-    method_BluetoothSocket_ctor = env->GetMethodID(clazz, "<init>", "(IZZLjava/lang/String;I)V");
+    method_BluetoothSocket_ctor = env->GetMethodID(clazz, "<init>", "(IIZZLjava/lang/String;I)V");
     return AndroidRuntime::registerNativeMethods(env,
         "android/bluetooth/BluetoothSocket", sMethods, NELEM(sMethods));
 }
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index f19fbbf..91449bc 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -229,7 +229,7 @@
 {
     int32_t err;
     CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Checking if column is a blob for %d,%d from %p", row, column, window);
+LOG_WINDOW("Checking if column is a blob or null for %d,%d from %p", row, column, window);
 
     field_slot_t field;
     err = window->read_field_slot(row, column, &field);
@@ -241,6 +241,54 @@
     return field.type == FIELD_TYPE_BLOB || field.type == FIELD_TYPE_NULL;
 }
 
+static jboolean isString_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+    int32_t err;
+    CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Checking if column is a string or null for %d,%d from %p", row, column, window);
+
+    field_slot_t field;
+    err = window->read_field_slot(row, column, &field);
+    if (err != 0) {
+        throwExceptionWithRowCol(env, row, column);
+        return NULL;
+    }
+
+    return field.type == FIELD_TYPE_STRING || field.type == FIELD_TYPE_NULL;
+}
+
+static jboolean isInteger_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+    int32_t err;
+    CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Checking if column is an integer for %d,%d from %p", row, column, window);
+
+    field_slot_t field;
+    err = window->read_field_slot(row, column, &field);
+    if (err != 0) {
+        throwExceptionWithRowCol(env, row, column);
+        return NULL;
+    }
+
+    return field.type == FIELD_TYPE_INTEGER;
+}
+
+static jboolean isFloat_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+    int32_t err;
+    CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Checking if column is a float for %d,%d from %p", row, column, window);
+
+    field_slot_t field;
+    err = window->read_field_slot(row, column, &field);
+    if (err != 0) {
+        throwExceptionWithRowCol(env, row, column);
+        return NULL;
+    }
+
+    return field.type == FIELD_TYPE_FLOAT;
+}
+
 static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column)
 {
     int32_t err;
@@ -326,11 +374,11 @@
         jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot");
         return NULL;
     }
-    
+
     jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField);
     if (buffer == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null");
-        return NULL;        
+        return NULL;
     }
     jchar* dst = env->GetCharArrayElements(buffer, NULL);
     uint8_t type = field.type;
@@ -338,7 +386,7 @@
     jcharArray newArray = NULL;
     if (type == FIELD_TYPE_STRING) {
         uint32_t size = field.data.buffer.size;
-        if (size > 0) {            
+        if (size > 0) {
 #if WINDOW_STORAGE_UTF8
             // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string
             String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1);
@@ -346,7 +394,7 @@
             if (strSize > bufferSize || dst == NULL) {
                 newArray = env->NewCharArray(strSize);
                 env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string());
-            } else {                
+            } else {
                 memcpy(dst, (jchar const *)utf16.string(), strSize * 2);
             }
             sizeCopied = strSize;
@@ -359,7 +407,7 @@
                 memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size);
             }
 #endif
-        } 
+        }
     } else if (type == FIELD_TYPE_INTEGER) {
         int64_t value;
         if (window->getLong(row, column, &value)) {
@@ -628,6 +676,9 @@
     {"putDouble_native", "(DII)Z", (void *)putDouble_native},
     {"freeLastRow_native", "()V", (void *)freeLastRow},
     {"putNull_native", "(II)Z", (void *)putNull_native},
+    {"isString_native", "(II)Z", (void *)isString_native},
+    {"isFloat_native", "(II)Z", (void *)isFloat_native},
+    {"isInteger_native", "(II)Z", (void *)isInteger_native},
 };
 
 int register_android_database_CursorWindow(JNIEnv * env)
@@ -646,7 +697,7 @@
         LOGE("Error locating fields");
         return -1;
     }
-    
+
     clazz =  env->FindClass("android/database/CharArrayBuffer");
     if (clazz == NULL) {
         LOGE("Can't find android/database/CharArrayBuffer");
diff --git a/core/jni/android_os_MemoryFile.cpp b/core/jni/android_os_MemoryFile.cpp
index 9450e15..8643393 100644
--- a/core/jni/android_os_MemoryFile.cpp
+++ b/core/jni/android_os_MemoryFile.cpp
@@ -39,17 +39,17 @@
 
     if (result < 0) {
         jniThrowException(env, "java/io/IOException", "ashmem_create_region failed");
-	return NULL;
+        return NULL;
     }
 
     return jniCreateFileDescriptor(env, result);
 }
 
 static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor,
-        jint length)
+        jint length, jint prot)
 {
     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
-    jint result = (jint)mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+    jint result = (jint)mmap(NULL, length, prot, MAP_SHARED, fd, 0);
     if (!result)
         jniThrowException(env, "java/io/IOException", "mmap failed");
     return result;
@@ -82,9 +82,7 @@
         return -1;
     }
 
-    jbyte* bytes = env->GetByteArrayElements(buffer, 0);
-    memcpy(bytes + destOffset, (const char *)address + srcOffset, count);
-    env->ReleaseByteArrayElements(buffer, bytes, 0);
+    env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset);
 
     if (unpinned) {
         ashmem_unpin_region(fd, 0, 0);
@@ -103,9 +101,7 @@
         return -1;
     }
 
-    jbyte* bytes = env->GetByteArrayElements(buffer, 0);
-    memcpy((char *)address + destOffset, bytes + srcOffset, count);
-    env->ReleaseByteArrayElements(buffer, bytes, 0);
+    env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset);
 
     if (unpinned) {
         ashmem_unpin_region(fd, 0, 0);
@@ -122,14 +118,36 @@
     }
 }
 
+static jboolean android_os_MemoryFile_is_ashmem_region(JNIEnv* env, jobject clazz,
+        jobject fileDescriptor) {
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+    // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region.
+    // ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel
+    // should return ENOTTY for all other valid file descriptors
+    int result = ashmem_get_size_region(fd);
+    if (result < 0) {
+        if (errno == ENOTTY) {
+            // ENOTTY means that the ioctl does not apply to this object,
+            // i.e., it is not an ashmem region.
+            return JNI_FALSE;
+        }
+        // Some other error, throw exception
+        jniThrowIOException(env, errno);
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
 static const JNINativeMethod methods[] = {
     {"native_open",  "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_MemoryFile_open},
-    {"native_mmap",  "(Ljava/io/FileDescriptor;I)I", (void*)android_os_MemoryFile_mmap},
+    {"native_mmap",  "(Ljava/io/FileDescriptor;II)I", (void*)android_os_MemoryFile_mmap},
     {"native_munmap", "(II)V", (void*)android_os_MemoryFile_munmap},
     {"native_close", "(Ljava/io/FileDescriptor;)V", (void*)android_os_MemoryFile_close},
     {"native_read",  "(Ljava/io/FileDescriptor;I[BIIIZ)I", (void*)android_os_MemoryFile_read},
     {"native_write", "(Ljava/io/FileDescriptor;I[BIIIZ)V", (void*)android_os_MemoryFile_write},
     {"native_pin",   "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin},
+    {"native_is_ashmem_region", "(Ljava/io/FileDescriptor;)Z",
+            (void*)android_os_MemoryFile_is_ashmem_region}
 };
 
 static const char* const kClassPathName = "android/os/MemoryFile";
diff --git a/core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png b/core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png
new file mode 100644
index 0000000..7abfd19
--- /dev/null
+++ b/core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png
Binary files differ
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index eb94812..9b9c2e4 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -125,4 +125,14 @@
         <item><xliff:g id="id">sync_failing</xliff:g></item>
         <item><xliff:g id="id">ime</xliff:g></item>
     </string-array>
+
+    <!-- Do not translate. Each string points to the component name of an ACTION_WEB_SEARCH
+         handling activity. On startup if there were no preferred ACTION_WEB_SEARCH handlers,
+         the first component from this list which is found to be installed is set as the
+         preferred activity. -->
+    <string-array name="default_web_search_providers">
+        <item>com.google.android.providers.genie/.GenieLauncher</item>
+        <item>com.android.googlesearch/.GoogleSearch</item>
+        <item>com.android.websearch/.Search.1</item>
+    </string-array>
 </resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 817a566..0a2d208 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -810,12 +810,19 @@
     <!-- The <code>supports-density</code> specifies a screen density that this
          package supports. Application can specify multiple densities it supports.
          <p>This appears as a child tag of the
-         {@link #AndroidManifestApplication application} tag. -->
-    <declare-styleable name="AndroidManifestSupportsDensity" parent="AndroidManifestApplication">
+         {@link #AndroidManifest manifest} tag. -->
+    <declare-styleable name="AndroidManifestSupportsDensity" parent="AndroidManifest">
         <!-- Required value of the density in dip (device independent pixel). -->
         <attr name="density" format="integer" />
     </declare-styleable>
 
+    <!-- The <code>expandable</code> specifies if this package supports screen metrics
+         other than 320x480 dip.
+         <p>This appears as a child tag of the
+         {@link #AndroidManifest manifest} tag. -->
+    <declare-styleable name="AndroidManifestExpandable" parent="AndroidManifest">
+    </declare-styleable>
+
     <!-- The <code>provider</code> tag declares a
          {@link android.content.ContentProvider} class that is available
          as part of the package's application components, supplying structured
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 96369f4..f67f04c 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -18,7 +18,7 @@
 */
 -->
 <resources>
-	<drawable name="screen_background_light">#ffffffff</drawable>
+	<drawable name="screen_background_light">#fff9f9f9</drawable>
 	<drawable name="screen_background_dark">#ff1a1a1a</drawable>
     <drawable name="status_bar_closed_default_background">#ff000000</drawable>
     <drawable name="status_bar_opened_default_background">#ff000000</drawable>
@@ -37,7 +37,7 @@
     <color name="dim_foreground_dark_inverse">#323232</color>
     <color name="dim_foreground_dark_inverse_disabled">#80323232</color>
     <color name="hint_foreground_dark">#808080</color>
-    <color name="background_light">#ffffffff</color>
+    <color name="background_light">#fff9f9f9</color>
     <color name="bright_foreground_light">#ff000000</color>
     <color name="bright_foreground_light_inverse">#ffffffff</color>
     <color name="bright_foreground_light_disabled">#80000000</color>
@@ -58,7 +58,7 @@
     <drawable name="editbox_dropdown_dark_frame">@drawable/editbox_dropdown_background_dark</drawable>
     <drawable name="editbox_dropdown_light_frame">@drawable/editbox_dropdown_background</drawable>
     
-    <drawable name="input_method_fullscreen_background">#ffffffff</drawable>
+    <drawable name="input_method_fullscreen_background">#fff9f9f9</drawable>
     
     <!-- For date picker widget -->
     <drawable name="selected_day_background">#ff0092f4</drawable>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 959ef6c..2aed931 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -148,6 +148,24 @@
     <!-- Meaning: unknown.  Example: Service was enabled for: Voice, PAD -->
     <string name="serviceClassPAD">PAD</string>
 
+    <!-- CDMA Roaming Indicator Strings (non ERI)--> <skip />
+    <!-- Default roaming indicator text -->
+    <string name="roamingText0">Roaming Indicator On</string>
+    <string name="roamingText1">Roaming Indicator Off</string>
+    <string name="roamingText2">Roaming Indicator Flashing</string>
+    <string name="roamingText3">Out of Neighborhood</string>
+    <string name="roamingText4">Out of Building</string>
+    <string name="roamingText5">Roaming - Preferred System</string>
+    <string name="roamingText6">Roaming - Available System</string>
+    <string name="roamingText7">Roaming - Alliance Partner</string>
+    <string name="roamingText8">Roaming - Premium Partner</string>
+    <string name="roamingText9">Roaming - Full Service Functionality</string>
+    <string name="roamingText10">Roaming - Partial Service Functionality</string>
+    <string name="roamingText11">Roaming Banner On</string>
+    <string name="roamingText12">Roaming Banner Off</string>
+    <string name="roamingTextSearching">Searching for Service</string>
+
+
     <!--
         {0} is one of "bearerServiceCode*"
         {1} is dialing number
diff --git a/core/res/res/xml/power_profile_default.xml b/core/res/res/xml/power_profile_default.xml
index d265b46..ceecb1a 100644
--- a/core/res/res/xml/power_profile_default.xml
+++ b/core/res/res/xml/power_profile_default.xml
@@ -22,7 +22,7 @@
   <item name="screen.on">30</item>
   <item name="bluetooth.active">103</item>
   <item name="bluetooth.on">5</item>
-  <item name="screen.full">144</item>
+  <item name="screen.full">114</item>
   <item name="wifi.on">23</item>
   <item name="wifi.active">200</item>
   <item name="wifi.scan">200</item>
@@ -33,4 +33,5 @@
   <item name="dsp.video">100</item>
   <item name="radio.on">3</item>
   <item name="radio.active">175</item>
+  <item name="gps.on">120</item>
 </device>
diff --git a/docs/html/guide/practices/ui_guidelines/activity_task_design.jd b/docs/html/guide/practices/ui_guidelines/activity_task_design.jd
index bec0e43..e2fc89c 100644
--- a/docs/html/guide/practices/ui_guidelines/activity_task_design.jd
+++ b/docs/html/guide/practices/ui_guidelines/activity_task_design.jd
@@ -34,11 +34,11 @@
   <li><a href=#tips>Design Tips
     <ol>
       <li><a href=#activity_not_reused_tip>Don't specify intent filters in an activity that won't be re-used</a></li>
-      <li><a href=#others_to_reuse_tip>Don't define your own URI schemes</a></li>
-      <li><a href=#reusing_tip>Handle where a re-used activity is missing</a></li>
+      <!-- <li><a href=#others_to_reuse_tip>Don't define your own URI schemes</a></li> -->
+      <li><a href=#reusing_tip>Handle case where no activity matches</a></li>
       <li><a href=#activity_launching_tip>Consider how to launch your activities</a></li>
       <li><a href=#activities_added_to_task_tip>Allow activities to add to current task</a></li>
-      <li><a href=#notifications_return_tip>Notifications should be easy to return from</a></li>
+      <li><a href=#notifications_get_back_tip>Notifications should let user easily get back</li>
       <li><a href=#use_notification_tip>Use the notification system</a></li>
       <li><a href=#taking_over_back_key>Don't take over BACK key unless you absolutely need to</a></li>
     </ol>
@@ -49,7 +49,6 @@
 
 <ol>
   <li><a href="{@docRoot}guide/topics/fundamentals.html">Application Fundamentals</a></li>
-  <li><a href="http://android-developers.blogspot.com/2009/05/activities-and-tasks.html">Activities and Tasks blog post</a></li>
 </ol>
 
 </div>
@@ -630,12 +629,12 @@
 <p>
   When the user takes an action on some data, such as touching a
   mailto:info@example.com link, they are actually initiating an Intent
-  object which then gets resolved to a particular component (we will
-  consider only activity components here). So, the result of a user
-  touching a mailto: link is an Intent object that the system tries to
-  match to an activity. If that Intent object was written explicitly
-  naming an activity (an <em>explicit intent</em>), then the system
-  immediately launches that activity in response to the user
+  object, or just an <em>intent</em>,  which then gets resolved to a
+  particular component (we consider only activity components here).
+  So, the result of a user touching a mailto: link is an Intent object 
+  that the system tries to match to an activity. If that Intent object was
+  written explicitly naming an activity (an <em>explicit intent</em>),
+  then the system immediately launches that activity in response to the user
   action. However, if that Intent object was written without naming an
   activity (an <em>implicit intent</em>), the system compares the Intent
   object to the <em>intent filters</em> of available activities. If more
@@ -872,12 +871,29 @@
 
 <p>
   Your applications can re-use activities made available from other
-  applications. In doing so, you cannot presume that external activity
-  will always be present &mdash; you must handle the case that the
-  external activity is not installed. Do this in the way you find most
-  appropriate, such as dimming the user control that accesses it (such
-  as a button or menu item), or displaying a message to the user that
-  sends them to the location to download it, such as the Market.
+  applications. In doing so, you cannot presume your intent will always
+  be resolved to a matching external activity &mdash; you must handle the case
+  where no application installed on the device can handle the intent.
+</p>
+
+<p>
+  You can either test that an activity matches the intent, which you can do
+  before starting the activity, or catch an exception if starting the
+  activity fails. Both approaches are descibed in the blog posting
+  <a href="http://android-developers.blogspot.com/2009/01/can-i-use-this-intent.html">Can
+  I use this Intent?</a>.
+</p>
+
+<p>
+  To test whether an intent can be resolved, your code can query the package manager.
+  The blog post provides an example in the isIntentAvailable() helper method.
+  You can perform this test when initializing the user interface.
+  For instance, you could disable the user control that initiates
+  the Intent object, or display a message to the user that lets them go
+  to a location, such as the Market, to download its application.
+  In this way, your code can start the activity (using either startActivity() 
+  or startActivityForResult()) only if the intent has tested to resolve
+  to an activity that is actually present.
 </p>
 
 <h3 id=activity_launching_tip>Consider how you want your activities to be launched or used by other applications</h3>
@@ -1054,15 +1070,14 @@
 </p>
 
     
-<h3 id="notifications_return_tip">Notifications should be easy for the user to return from</h3>
-
+<h3 id="notifications_get_back_tip">Notifications should let the user easily get back to the previous activity</h3>
 <p>
-  Applications that are in the background or haven't been run can
-  send out notifications to the user letting them know about events
-  of interest. For example, Calendar can send out notifications of
-  upcoming events, and Email can send out notifications when new
-  messages arrive. One of the user interface rules is that when the
-  user is in activity A and gets a notification for activity B and
+  Applications that are in the background or not running can have
+  services that send out notifications to the user letting them know about
+  events of interest. Two examples are Calendar, which can send out notifications of
+  upcoming events, and Email, which can send out notifications when new
+  messages arrive. One of the user interface guidelines is that when the
+  user is in activity A, gets a notification for activity B and
   picks that notification, when they press the BACK key, they should
   go back to activity A.&nbsp;
 </p>
@@ -1108,11 +1123,11 @@
 
   <ul>
     <li>
-      <b>The application has a dedicated activity for
-      notification</b> - For example, when the user receives a
-      Calendar notification, the act of selecting that
+      <b>The chosen activity is dedicated for notification only</b> -
+      For example, when the user receives a
+      Calendar notification, choosing that
       notification starts a special activity that displays a list
-      of upcoming calendar events &mdash; a view available only
+      of upcoming calendar events &mdash; this view is available only
       from the notification, not through the Calendar's own user
       interface. After viewing this upcoming event, to ensure that
       the user pressing the BACK key will return to the activity
@@ -1140,25 +1155,25 @@
     </li>
 
     <li>
-      <b>The user choosing the notification brings the activity to
+      <b>The chosen activity is not dedicated, but always comes to
       the foreground in its initial state</b> - For example, in
-      response to a notification, the Gmail application is brought
-      to the foreground presenting the list of conversations. You
-      do this by having the user's response to the notification
-      trigger an intent to launch the activity with the clear top
-      flag set. (That is, you put {@link
+      response to a notification, when the Gmail application comes
+      to the foreground, it always presents the list of conversations.
+      You can ensure this happens by setting a "clear top" flag in the
+      intent that the notification triggers.  This ensures that when the
+      activity is launched, it displays its initial activity, preventing
+      Gmail from coming to the foreground in whatever state the user last
+      happened to be viewing it. (To do this, you put {@link
       android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP
-      FLAG_ACTIVITY_CLEAR_TOP} in the intent you pass to
-      startActivity()). This prevents Gmail from coming to the
-      foreground in whatever state the user last happened to be
-      viewing it.
+      FLAG_ACTIVITY_CLEAR_TOP} in the intent you pass to startActivity()). 
     </li>
   </ul>
 
 <p>
   There are other ways to handle notifications, such as bringing the
-  activity to the foreground set to display specific data, such as the
-  ongoing text message thread of a particular person.
+  activity to the foreground, set to display specific data, such as
+  displaying the text message thread for the person who just sent a
+  new text message.
 </p>
 
 <p>
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 9e88d7e..e5a9aab 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -18,18 +18,18 @@
 
 import android.content.res.AssetManager;
 import android.content.res.Resources;
-import android.util.TypedValue;
 import android.util.DisplayMetrics;
+import android.util.TypedValue;
 
 import java.io.BufferedInputStream;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.io.IOException;
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
 
 /**
- * Creates Bitmap objects from various sources, including files, streams, 
- * and byte-arrays. 
+ * Creates Bitmap objects from various sources, including files, streams,
+ * and byte-arrays.
  */
 public class BitmapFactory {
     public static class Options {
@@ -62,7 +62,7 @@
          * Also, powers of 2 are often faster/easier for the decoder to honor.
          */
         public int inSampleSize;
-        
+
         /**
          * If this is non-null, the decoder will try to decode into this
          * internal configuration. If it is null, or the request cannot be met,
@@ -71,7 +71,7 @@
          * as if it has per-pixel alpha (requiring a config that also does).
          */
         public Bitmap.Config inPreferredConfig;
-        
+
         /**
          * If dither is true, the decoder will atttempt to dither the decoded
          * image.
@@ -117,8 +117,6 @@
          * explicitly make a copy of the input data, and keep that. Even if
          * sharing is allowed, the implementation may still decide to make a
          * deep copy of the input data.
-         *
-         * @hide pending API council approval
          */
         public boolean inPurgeable;
 
@@ -127,8 +125,6 @@
          * false, then this field is ignored. If inPurgeable is true, then this
          * field determines whether the bitmap can share a reference to the
          * input data (inputstream, array, etc.) or if it must make a deep copy.
-         *
-         * @hide pending API council approval
          */
         public boolean inInputShareable;
 
@@ -151,12 +147,12 @@
          * If not know, or there is an error, it is set to null.
          */
         public String outMimeType;
-        
+
         /**
          * Temp storage to use for decoding.  Suggest 16K or so.
          */
         public byte[] inTempStorage;
-        
+
         private native void requestCancel();
 
         /**
@@ -167,7 +163,7 @@
          * if the operation is canceled.
          */
         public boolean mCancel;
-        
+
         /**
          *  This can be called from another thread while this options object is
          *  inside a decode... call. Calling this will notify the decoder that
@@ -249,7 +245,7 @@
             if (opts.inDensity == 0) {
                 opts.inDensity = density == TypedValue.DENSITY_DEFAULT ?
                         DisplayMetrics.DEFAULT_DENSITY : density;
-            }            
+            }
             float scale = opts.inDensity / (float) DisplayMetrics.DEFAULT_DENSITY;
 
             if (opts.inScaled || isNinePatch) {
@@ -291,7 +287,7 @@
      */
     public static Bitmap decodeResource(Resources res, int id, Options opts) {
         Bitmap bm = null;
-        
+
         try {
             final TypedValue value = new TypedValue();
             final InputStream is = res.openRawResource(id, value);
@@ -306,7 +302,7 @@
         }
         return bm;
     }
-    
+
     /**
      * Decode an image referenced by a resource ID.
      *
@@ -337,7 +333,7 @@
         }
         return nativeDecodeByteArray(data, offset, length, opts);
     }
-    
+
     /**
      * Decode an immutable bitmap from the specified byte array.
      *
@@ -350,13 +346,13 @@
     public static Bitmap decodeByteArray(byte[] data, int offset, int length) {
         return decodeByteArray(data, offset, length, null);
     }
-    
+
     /**
      * Decode an input stream into a bitmap. If the input stream is null, or
      * cannot be used to decode a bitmap, the function returns null.
      * The stream's position will be where ever it was after the encoded data
      * was read.
-     * 
+     *
      * @param is The input stream that holds the raw data to be decoded into a
      *           bitmap.
      * @param outPadding If not null, return the padding rect for the bitmap if
@@ -375,7 +371,7 @@
         if (is == null) {
             return null;
         }
-        
+
         // we need mark/reset to work properly
 
         if (!is.markSupported()) {
@@ -413,7 +409,7 @@
      * cannot be used to decode a bitmap, the function returns null.
      * The stream's position will be where ever it was after the encoded data
      * was read.
-     * 
+     *
      * @param is The input stream that holds the raw data to be decoded into a
      *           bitmap.
      * @return The decoded bitmap, or null if the image data could not be
@@ -441,7 +437,7 @@
     public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
         return nativeDecodeFileDescriptor(fd, outPadding, opts);
     }
-    
+
     /**
      * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
      * return null. The position within the descriptor will not be changed when
@@ -453,7 +449,7 @@
     public static Bitmap decodeFileDescriptor(FileDescriptor fd) {
         return nativeDecodeFileDescriptor(fd, null, null);
     }
-    
+
     private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
             Rect padding, Options opts);
     private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
diff --git a/include/utils/backup_helpers.h b/include/utils/BackupHelpers.h
similarity index 100%
rename from include/utils/backup_helpers.h
rename to include/utils/BackupHelpers.h
diff --git a/include/utils/LogSocket.h b/include/utils/LogSocket.h
deleted file mode 100644
index 01fbfb5..0000000
--- a/include/utils/LogSocket.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* utils/LogSocket.h
-** 
-** Copyright 2008, The Android Open Source Project
-**
-** This file is dual licensed.  It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
-
-#ifndef _UTILS_LOGSOCKET_H
-#define _UTILS_LOGSOCKET_H
-
-#define SOCKET_CLOSE_LOCAL 0
-
-void add_send_stats(int fd, int send);
-void add_recv_stats(int fd, int recv);
-void log_socket_close(int fd, short reason);
-void log_socket_connect(int fd, unsigned int ip, unsigned short port);
-
-#endif /* _UTILS_LOGSOCKET_H */
diff --git a/include/utils/Socket.h b/include/utils/Socket.h
deleted file mode 100644
index 8b7f4061..0000000
--- a/include/utils/Socket.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Socket class.  Modeled after Java classes.
-//
-#ifndef _RUNTIME_SOCKET_H
-#define _RUNTIME_SOCKET_H
-
-#include <utils/inet_address.h>
-#include <sys/types.h>
-
-namespace android {
-
-/*
- * Basic socket class, needed to abstract away the differences between
- * BSD sockets and WinSock.  This establishes a streaming network
- * connection (TCP/IP) to somebody.
- */
-class Socket {
-public:
-    Socket(void);
-    ~Socket(void);
-
-    // Create a connection to somewhere.
-    // Return 0 on success.
-    int connect(const char* host, int port);
-    int connect(const InetAddress* addr, int port);
-
-
-    // Close the socket.  Don't try to use this object again after
-    // calling this.  Returns false on failure.
-    bool close(void);
-
-    // If we created the socket without an address, we can use these
-    // to finish the connection.  Returns 0 on success.
-    int bind(const SocketAddress& bindPoint);
-    int connect(const SocketAddress& endPoint);
-
-    // Here we deviate from the traditional object-oriented fanciness
-    // and just provide read/write operators instead of getters for
-    // objects that abstract a stream.
-    //
-    // Standard read/write semantics.
-    int read(void* buf, ssize_t len) const;
-    int write(const void* buf, ssize_t len) const;
-
-    // This must be called once, at program startup.
-    static bool bootInit(void);
-    static void finalShutdown(void);
-
-private:
-    // Internal function that establishes a connection.
-    int doConnect(const InetSocketAddress& addr);
-
-    unsigned long   mSock;      // holds SOCKET or int
-
-    static bool     mBootInitialized;
-};
-
-
-// debug -- unit tests
-void TestSockets(void);
-
-}; // namespace android
-
-#endif // _RUNTIME_SOCKET_H
diff --git a/include/utils/StringArray.h b/include/utils/StringArray.h
new file mode 100644
index 0000000..c244587
--- /dev/null
+++ b/include/utils/StringArray.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+//
+// Sortable array of strings.  STL-ish, but STL-free.
+//  
+#ifndef _LIBS_UTILS_STRING_ARRAY_H
+#define _LIBS_UTILS_STRING_ARRAY_H
+
+#include <stdlib.h>
+#include <string.h>
+
+namespace android {
+
+//
+// An expanding array of strings.  Add, get, sort, delete.
+//
+class StringArray {
+public:
+    StringArray();
+    virtual ~StringArray();
+
+    //
+    // Add a string.  A copy of the string is made.
+    //
+    bool push_back(const char* str);
+
+    //
+    // Delete an entry.
+    //
+    void erase(int idx);
+
+    //
+    // Sort the array.
+    //
+    void sort(int (*compare)(const void*, const void*));
+    
+    //
+    // Pass this to the sort routine to do an ascending alphabetical sort.
+    //
+    static int cmpAscendingAlpha(const void* pstr1, const void* pstr2);
+    
+    //
+    // Get the #of items in the array.
+    //
+    inline int size(void) const { return mCurrent; }
+
+    //
+    // Return entry N.
+    // [should use operator[] here]
+    //
+    const char* getEntry(int idx) const {
+        return (unsigned(idx) >= unsigned(mCurrent)) ? NULL : mArray[idx];
+    }
+
+    //
+    // Set entry N to specified string.
+    // [should use operator[] here]
+    //
+    void setEntry(int idx, const char* str);
+
+private:
+    int     mMax;
+    int     mCurrent;
+    char**  mArray;
+};
+
+}; // namespace android
+
+#endif // _LIBS_UTILS_STRING_ARRAY_H
diff --git a/include/utils/inet_address.h b/include/utils/inet_address.h
deleted file mode 100644
index dbd8672..0000000
--- a/include/utils/inet_address.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Internet address classes.  Modeled after Java classes.
-//
-#ifndef _RUNTIME_INET_ADDRESS_H
-#define _RUNTIME_INET_ADDRESS_H
-
-#ifdef HAVE_ANDROID_OS
-#error DO NOT USE THIS FILE IN THE DEVICE BUILD
-#endif
-
-
-namespace android {
-
-/*
- * This class holds Internet addresses.  Perhaps more useful is its
- * ability to look up addresses by name.
- *
- * Invoke one of the static factory methods to create a new object.
- */
-class InetAddress {
-public:
-    virtual ~InetAddress(void);
-
-    // create from w.x.y.z or foo.bar.com notation
-    static InetAddress* getByName(const char* host);
-
-    // copy-construction
-    InetAddress(const InetAddress& orig);
-
-    const void* getAddress(void) const { return mAddress; }
-    int getAddressLength(void) const { return mLength; }
-    const char* getHostName(void) const { return mName; }
-
-private:
-    InetAddress(void);
-    // assignment (private)
-    InetAddress& operator=(const InetAddress& addr);
-
-    // use a void* here so we don't have to expose actual socket headers
-    void*       mAddress;   // this is really a ptr to sockaddr_in
-    int         mLength;
-    char*       mName;
-};
-
-
-/*
- * Base class for socket addresses.
- */
-class SocketAddress {
-public:
-    SocketAddress() {}
-    virtual ~SocketAddress() {}
-};
-
-
-/*
- * Internet address class.  This combines an InetAddress with a port.
- */
-class InetSocketAddress : public SocketAddress {
-public:
-    InetSocketAddress() :
-        mAddress(0), mPort(-1)
-        {}
-    ~InetSocketAddress(void) {
-        delete mAddress;
-    }
-
-    // Create an address with a host wildcard (useful for servers).
-    bool create(int port);
-    // Create an address with the specified host and port.
-    bool create(const InetAddress* addr, int port);
-    // Create an address with the specified host and port.  Does the
-    // hostname lookup.
-    bool create(const char* host, int port);
-
-    const InetAddress* getAddress(void) const { return mAddress; }
-    const int getPort(void) const { return mPort; }
-    const char* getHostName(void) const { return mAddress->getHostName(); }
-
-private:
-    InetAddress* mAddress;
-    int         mPort;
-};
-
-}; // namespace android
-
-#endif // _RUNTIME_INET_ADDRESS_H
diff --git a/include/utils/string_array.h b/include/utils/string_array.h
deleted file mode 100644
index 064dda2..0000000
--- a/include/utils/string_array.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Sortable array of strings.  STL-ish, but STL-free.
-//  
-#ifndef _LIBS_UTILS_STRING_ARRAY_H
-#define _LIBS_UTILS_STRING_ARRAY_H
-
-#include <stdlib.h>
-#include <string.h>
-
-namespace android {
-
-//
-// An expanding array of strings.  Add, get, sort, delete.
-//
-class StringArray {
-public:
-    StringArray()
-        : mMax(0), mCurrent(0), mArray(NULL)
-        {}
-    virtual ~StringArray() {
-        for (int i = 0; i < mCurrent; i++)
-            delete[] mArray[i];
-        delete[] mArray;
-    }
-
-    //
-    // Add a string.  A copy of the string is made.
-    //
-    bool push_back(const char* str) {
-        if (mCurrent >= mMax) {
-            char** tmp;
-
-            if (mMax == 0)
-                mMax = 16;      // initial storage
-            else
-                mMax *= 2;
-
-            tmp = new char*[mMax];
-            if (tmp == NULL)
-                return false;
-
-            memcpy(tmp, mArray, mCurrent * sizeof(char*));
-            delete[] mArray;
-            mArray = tmp;
-        }
-
-        int len = strlen(str);
-        mArray[mCurrent] = new char[len+1];
-        memcpy(mArray[mCurrent], str, len+1);
-        mCurrent++;
-
-        return true;
-    }
-
-    //
-    // Delete an entry.
-    //
-    void erase(int idx) {
-        if (idx < 0 || idx >= mCurrent)
-            return;
-        delete[] mArray[idx];
-        if (idx < mCurrent-1) {
-            memmove(&mArray[idx], &mArray[idx+1],
-                (mCurrent-1 - idx) * sizeof(char*));
-        }
-        mCurrent--;
-    }
-
-    //
-    // Sort the array.
-    //
-    void sort(int (*compare)(const void*, const void*)) {
-        qsort(mArray, mCurrent, sizeof(char*), compare);
-    }
-
-    //
-    // Pass this to the sort routine to do an ascending alphabetical sort.
-    //
-    static int cmpAscendingAlpha(const void* pstr1, const void* pstr2) {
-        return strcmp(*(const char**)pstr1, *(const char**)pstr2);
-    }
-
-    //
-    // Get the #of items in the array.
-    //
-    inline int size(void) const { return mCurrent; }
-
-    //
-    // Return entry N.
-    // [should use operator[] here]
-    //
-    const char* getEntry(int idx) const {
-        if (idx < 0 || idx >= mCurrent)
-            return NULL;
-        return mArray[idx];
-    }
-
-    //
-    // Set entry N to specified string.
-    // [should use operator[] here]
-    //
-    void setEntry(int idx, const char* str) {
-        if (idx < 0 || idx >= mCurrent)
-            return;
-        delete[] mArray[idx];
-        int len = strlen(str);
-        mArray[idx] = new char[len+1];
-        memcpy(mArray[idx], str, len+1);
-    }
-
-private:
-    int     mMax;
-    int     mCurrent;
-    char**  mArray;
-};
-
-}; // namespace android
-
-#endif // _LIBS_UTILS_STRING_ARRAY_H
diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk
index 6f72802..6ff39a4 100644
--- a/libs/rs/Android.mk
+++ b/libs/rs/Android.mk
@@ -117,6 +117,7 @@
 	libnativehelper \
 	libRS \
 	libcutils \
+    libsgl \
 	libutils \
 	libui
 
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 378fccc..2b33419 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -86,14 +86,14 @@
     RS_ELEMENT_USER_I32,
     RS_ELEMENT_USER_FLOAT, 
 
-    RS_ELEMENT_A_8, 
-    RS_ELEMENT_RGB_565, 
-    RS_ELEMENT_RGBA_5551, 
-    RS_ELEMENT_RGBA_4444, 
-    RS_ELEMENT_RGB_888, 
-    RS_ELEMENT_RGBA_8888, 
+    RS_ELEMENT_A_8,          // 7
+    RS_ELEMENT_RGB_565,      // 8
+    RS_ELEMENT_RGBA_5551,    // 9
+    RS_ELEMENT_RGBA_4444,    // 10
+    RS_ELEMENT_RGB_888,      // 11
+    RS_ELEMENT_RGBA_8888,    // 12
 
-    RS_ELEMENT_INDEX_16, 
+    RS_ELEMENT_INDEX_16, //13
     RS_ELEMENT_INDEX_32, 
     RS_ELEMENT_XY_F32, 
     RS_ELEMENT_XYZ_F32, 
diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h
index dfca56c..1bf5f22 100644
--- a/libs/rs/RenderScriptEnv.h
+++ b/libs/rs/RenderScriptEnv.h
@@ -87,7 +87,7 @@
     void (*drawRect)(void *con, int32_t x1, int32_t x2, int32_t y1, int32_t y2);
 } rsc_FunctionTable;
 
-typedef void (*rsc_RunScript)(void *con, const rsc_FunctionTable *, uint32_t launchID);
+typedef int (*rsc_RunScript)(void *con, const rsc_FunctionTable *, uint32_t launchID);
 
 
 /* EnableCap */
diff --git a/libs/rs/java/Fountain/res/raw/fountain.c b/libs/rs/java/Fountain/res/raw/fountain.c
index 76f8dcf..8b473ab 100644
--- a/libs/rs/java/Fountain/res/raw/fountain.c
+++ b/libs/rs/java/Fountain/res/raw/fountain.c
@@ -1,21 +1,21 @@
 // Fountain test script
 
-main(con, ft, launchID) {
+int main(void* con, int ft, int launchID) {
     int count, touch, x, y, rate, maxLife, lifeShift;
     int life;
     int ct, ct2;
     int newPart;
     int drawCount;
     int dx, dy, idx;
-    int partPtr;
-    int vertPtr;
     int posx,posy;
     int c;
+    int srcIdx;
+    int dstIdx;
 
     count = loadI32(con, 0, 1);
     touch = loadI32(con, 0, 2);
     x = loadI32(con, 0, 3);
-    y = 480 - loadI32(con, 0, 4);
+    y = loadI32(con, 0, 4);
 
     rate = 4;
     maxLife = (count / rate) - 1;
@@ -53,59 +53,52 @@
         storeI32(con, 2, 0, newPart);
     }
 
-    // Emulate intrinsic perf...
-    partPtr = loadVp(con, 2, 4);
-    vertPtr = loadVp(con, 1, 0);
-
     drawCount = 0;
     for (ct=0; ct < count; ct++) {
-        //int srcIdx = ct * 5 + 1;
-        //int dstIdx = ct * 3 * 3;
+        srcIdx = ct * 5 + 1;
 
-        dx = * (int* )(partPtr + 0); //loadEnvI32(con, 2, srcIdx);
-        dy = * (int* )(partPtr + 4); //loadEnvI32(con, 2, srcIdx + 1);
-        life = * (int* )(partPtr + 8); //loadEnvI32(con, 2, srcIdx + 2);
-        posx = * (int* )(partPtr + 12); //loadEnvI32(con, 2, srcIdx + 3);
-        posy = * (int* )(partPtr + 16); //loadEnvI32(con, 2, srcIdx + 4);
+        dx = loadI32(con, 2, srcIdx);
+        dy = loadI32(con, 2, srcIdx + 1);
+        life = loadI32(con, 2, srcIdx + 2);
+        posx = loadI32(con, 2, srcIdx + 3);
+        posy = loadI32(con, 2, srcIdx + 4);
 
         if (life) {
-            if (posy > 0) {
+            if (posy < (480 << 16)) {
+                dstIdx = drawCount * 9;
                 c = 0xffafcf | ((life >> lifeShift) << 24);
 
-                * (int* )(vertPtr) = c; //storeEnvU32(con, 1, dstIdx, c);
-                * (int* )(vertPtr + 4) = posx; //storeEnvI32(con, 1, dstIdx + 1, posx);
-                * (int* )(vertPtr + 8) = posy; //storeEnvI32(con, 1, dstIdx + 2, posy);
+                storeU32(con, 1, dstIdx, c);
+                storeI32(con, 1, dstIdx + 1, posx);
+                storeI32(con, 1, dstIdx + 2, posy);
 
-                * (int* )(vertPtr + 12) = c; //storeEnvU32(con, 1, dstIdx + 3, c);
-                * (int* )(vertPtr + 16) = posx + 0x10000; //storeEnvI32(con, 1, dstIdx + 4, posx + 0x10000);
-                * (int* )(vertPtr + 20) = posy + dy * 4; //storeEnvI32(con, 1, dstIdx + 5, posy);
+                storeU32(con, 1, dstIdx + 3, c);
+                storeI32(con, 1, dstIdx + 4, posx + 0x10000);
+                storeI32(con, 1, dstIdx + 5, posy + dy * 4);
 
-                * (int* )(vertPtr + 24) = c; //storeEnvU32(con, 1, dstIdx + 6, c);
-                * (int* )(vertPtr + 28) = posx - 0x10000; //storeEnvI32(con, 1, dstIdx + 7, posx + 0x0800);
-                * (int* )(vertPtr + 32) = posy + dy * 4; //storeEnvI32(con, 1, dstIdx + 8, posy + 0x10000);
-
-                vertPtr = vertPtr + 36;
+                storeU32(con, 1, dstIdx + 6, c);
+                storeI32(con, 1, dstIdx + 7, posx - 0x10000);
+                storeI32(con, 1, dstIdx + 8, posy + dy * 4);
                 drawCount ++;
             } else {
-                if (dy < 0) {
+                if (dy > 0) {
                     dy = (-dy) >> 1;
                 }
             }
 
             posx = posx + dx;
             posy = posy + dy;
-            dy = dy - 0x400;
+            dy = dy + 0x400;
             life --;
 
-            * (int* )(partPtr + 0) = dx; //storeEnvI32(con, 2, srcIdx, dx);
-            * (int* )(partPtr + 4) = dy; //storeEnvI32(con, 2, srcIdx + 1, dy);
-            * (int* )(partPtr + 8) = life; //storeEnvI32(con, 2, srcIdx + 2, life);
-            * (int* )(partPtr + 12) = posx; //storeEnvI32(con, 2, srcIdx + 3, posx);
-            * (int* )(partPtr + 16) = posy; //storeEnvI32(con, 2, srcIdx + 4, posy);
+            //storeI32(con, 2, srcIdx, dx);
+            storeI32(con, 2, srcIdx + 1, dy);
+            storeI32(con, 2, srcIdx + 2, life);
+            storeI32(con, 2, srcIdx + 3, posx);
+            storeI32(con, 2, srcIdx + 4, posy);
         }
-
-        partPtr = partPtr + 20;
     }
 
     drawTriangleArray(con, loadI32(con, 0, 5), drawCount);
+    return 1;
 }
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java b/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java
index cf16cec..796fe35 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java
@@ -83,9 +83,8 @@
     native private int  nAllocationCreateTyped(int type);
     native private int  nAllocationCreatePredefSized(int predef, int count);
     native private int  nAllocationCreateSized(int elem, int count);
-    native private int  nAllocationCreateFromBitmap(int w, int h, int dstFmt, int srcFmt, boolean genMips, int[] data);
+    native private int  nAllocationCreateFromBitmap(int dstFmt, boolean genMips, Bitmap bmp);
 
-    //native private int  nAllocationCreateFromBitmap(type.mID);
     native private void nAllocationUploadToTexture(int alloc, int baseMioLevel);
     native private void nAllocationDestroy(int alloc);
     native private void nAllocationData(int id, int[] d);
@@ -190,21 +189,19 @@
         USER_I32 (5),
         USER_FLOAT (6),
 
-        A_8 (7),
-        RGB_565 (8),
-        RGBA_5551 (9),
-        RGBA_4444 (10),
-        RGB_888 (11),
-        RGBA_8888 (12),
+        A_8                (7),
+        RGB_565            (8),
+        RGB_888            (12),
+        RGBA_5551          (9),
+        RGBA_4444          (10),
+        RGBA_8888          (13),
 
-        INDEX_16 (13),
-        INDEX_32 (14),
-        XY_F32 (15),
-        XYZ_F32 (16),
-        ST_XY_F32 (17),
-        ST_XYZ_F32 (18),
-        NORM_XYZ_F32 (19),
-        NORM_ST_XYZ_F32 (20);
+        INDEX_16           (16),
+        INDEX_32           (17),
+        XY_F32             (18),
+        XYZ_F32            (19),
+        ST_XY_F32          (20),
+        ST_XYZ_F32         (21);
 
         int mID;
         ElementPredefined(int id) {
@@ -475,41 +472,7 @@
     }
 
     public Allocation allocationCreateFromBitmap(Bitmap b, ElementPredefined dstFmt, boolean genMips) {
-        int w = b.getWidth();
-        int h = b.getHeight();
-        int[] data = new int[w * h];
-
-        int outPtr = 0;
-        for(int y=0; y < h; y++) {
-            for(int x=0; x < w; x++) {
-                data[outPtr] = b.getPixel(x, y);
-                outPtr++;
-            }
-        }
-
-        int srcFmt = 0;
-        /*
-        switch(b.getConfig()) {
-        case ALPHA_8:
-            srcFmt = ElementPredefined.A_8.mID;
-            break;
-        case ARGB_4444:
-            srcFmt = ElementPredefined.RGBA_4444.mID;
-            break;
-        case ARGB_8888:
-            srcFmt = ElementPredefined.RGBA_8888.mID;
-            break;
-        case RGB_565:
-            srcFmt = ElementPredefined.RGB_565.mID;
-            break;
-        default:
-            Log.e(LOG_TAG, "allocationCreateFromBitmap, unknown bitmap format");
-        }
-        */
-
-        srcFmt = ElementPredefined.RGBA_8888.mID;
-
-        int id = nAllocationCreateFromBitmap(w, h, dstFmt.mID, srcFmt, genMips, data);
+        int id = nAllocationCreateFromBitmap(dstFmt.mID, genMips, b); 
         return new Allocation(id);
     }
 
diff --git a/libs/rs/jni/RenderScript_jni.cpp b/libs/rs/jni/RenderScript_jni.cpp
index 4af58a6..61866b7 100644
--- a/libs/rs/jni/RenderScript_jni.cpp
+++ b/libs/rs/jni/RenderScript_jni.cpp
@@ -26,6 +26,8 @@
 #include <ui/EGLNativeWindowSurface.h>
 #include <ui/Surface.h>
 
+#include <core/SkBitmap.h>
+
 #include "jni.h"
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
@@ -48,12 +50,15 @@
     env->ThrowNew(npeClazz, msg);
 }
 
-static jfieldID gContextId;
+static jfieldID gContextId = 0;
+static jfieldID gNativeBitmapID = 0;
 
 static void _nInit(JNIEnv *_env, jclass _this)
 {
-    LOGE("_nInit");
     gContextId             = _env->GetFieldID(_this, "mContext", "I");
+
+    jclass bitmapClass = _env->FindClass("android/graphics/Bitmap");
+    gNativeBitmapID = _env->GetFieldID(bitmapClass, "mNativeBitmap", "I");
 }
 
 
@@ -218,19 +223,48 @@
     rsAllocationUploadToTexture((RsAllocation)a, mip);
 }
 
-static int
-nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, jint w, jint h, jint dstFmt, jint srcFmt, jboolean genMips, jintArray data)
+static RsElementPredefined SkBitmapToPredefined(SkBitmap::Config cfg)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocationCreateFromBitmap, con(%p), w(%i), h(%i), dstFmt(%i), srcFmt(%i), mip(%i), len(%i)", con, w, h, dstFmt, srcFmt, genMips, len);
+    switch (cfg) {
+    case SkBitmap::kA8_Config:
+        return RS_ELEMENT_A_8;
+    case SkBitmap::kARGB_4444_Config:
+        return RS_ELEMENT_RGBA_4444;
+    case SkBitmap::kARGB_8888_Config:
+        return RS_ELEMENT_RGBA_8888;
+    case SkBitmap::kRGB_565_Config:
+        return RS_ELEMENT_RGB_565;
 
-    jint *ptr = _env->GetIntArrayElements(data, NULL);
-    jint id = (jint)rsAllocationCreateFromBitmap(w, h, (RsElementPredefined)dstFmt, (RsElementPredefined)srcFmt, genMips, ptr);
-    _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
-    return id;
+    default:
+        break;
+    }
+    // If we don't have a conversion mark it as a user type.
+    LOGE("Unsupported bitmap type");
+    return RS_ELEMENT_USER_U8;
 }
 
+static int
+nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, jint dstFmt, jboolean genMips, jobject jbitmap)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    SkBitmap const * nativeBitmap =
+            (SkBitmap const *)_env->GetIntField(jbitmap, gNativeBitmapID);
+    const SkBitmap& bitmap(*nativeBitmap);
+    SkBitmap::Config config = bitmap.getConfig();
+
+    RsElementPredefined e = SkBitmapToPredefined(config);
+
+    if (e != RS_ELEMENT_USER_U8) {
+        bitmap.lockPixels();
+        const int w = bitmap.width();
+        const int h = bitmap.height();
+        const void* ptr = bitmap.getPixels();
+        jint id = (jint)rsAllocationCreateFromBitmap(w, h, (RsElementPredefined)dstFmt, e, genMips, ptr);
+        bitmap.unlockPixels();
+        return id;
+    }
+    return 0;
+}
 
 
 static void
@@ -795,7 +829,7 @@
 {"nAllocationCreateTyped",         "(I)I",                                 (void*)nAllocationCreateTyped },
 {"nAllocationCreatePredefSized",   "(II)I",                                (void*)nAllocationCreatePredefSized },
 {"nAllocationCreateSized",         "(II)I",                                (void*)nAllocationCreateSized },
-{"nAllocationCreateFromBitmap",    "(IIIIZ[I)I",                           (void*)nAllocationCreateFromBitmap },
+{"nAllocationCreateFromBitmap",    "(IZLandroid/graphics/Bitmap;)I",       (void*)nAllocationCreateFromBitmap },
 {"nAllocationUploadToTexture",     "(II)V",                                (void*)nAllocationUploadToTexture },
 {"nAllocationDestroy",             "(I)V",                                 (void*)nAllocationDestroy },
 {"nAllocationData",                "(I[I)V",                               (void*)nAllocationData_i },
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 7b8bc80..3a01a75 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -47,6 +47,7 @@
 
 Allocation::~Allocation()
 {
+    LOGE("Allocation %p destryed", this);
 }
 
 void Allocation::setCpuWritable(bool)
@@ -77,6 +78,13 @@
 
     //LOGE("uploadToTexture  %i,  lod %i", mTextureID, lodOffset);
 
+    GLenum type = mType->getElement()->getGLType();
+    GLenum format = mType->getElement()->getGLFormat();
+
+    if (!type || !format) {
+        return;
+    }
+
     if (!mTextureID) {
         glGenTextures(1, &mTextureID);
     }
@@ -87,9 +95,9 @@
         adapt.setLOD(lod+lodOffset);
 
         uint16_t * ptr = static_cast<uint16_t *>(adapt.getElement(0,0));
-        glTexImage2D(GL_TEXTURE_2D, lod, GL_RGB, 
-                     adapt.getDimX(), adapt.getDimY(), 
-                     0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, ptr);
+        glTexImage2D(GL_TEXTURE_2D, lod, format,
+                     adapt.getDimX(), adapt.getDimY(),
+                     0, format, type, ptr);
     }
 }
 
@@ -121,7 +129,7 @@
     memcpy(ptr, data, count * eSize);
 }
 
-void Allocation::subData(uint32_t xoff, uint32_t yoff, 
+void Allocation::subData(uint32_t xoff, uint32_t yoff,
              uint32_t w, uint32_t h, const void *data)
 {
     uint32_t eSize = mType->getElementSizeBytes();
@@ -147,7 +155,7 @@
 
 
 /////////////////
-// 
+//
 
 
 namespace android {
@@ -192,7 +200,7 @@
 {
 }
 
-static void mip(const Adapter2D &out, const Adapter2D &in)
+static void mip565(const Adapter2D &out, const Adapter2D &in)
 {
     uint32_t w = out.getDimX();
     uint32_t h = out.getDimY();
@@ -203,7 +211,26 @@
         const uint16_t *i2 = static_cast<uint16_t *>(in.getElement(0, y*2+1));
 
         for (uint32_t x=0; x < h; x++) {
-            *oPtr = rsBoxFilter565(i1[0], i1[2], i2[0], i2[1]);
+            *oPtr = rsBoxFilter565(i1[0], i1[1], i2[0], i2[1]);
+            oPtr ++;
+            i1 += 2;
+            i2 += 2;
+        }
+    }
+}
+
+static void mip8888(const Adapter2D &out, const Adapter2D &in)
+{
+    uint32_t w = out.getDimX();
+    uint32_t h = out.getDimY();
+
+    for (uint32_t y=0; y < w; y++) {
+        uint32_t *oPtr = static_cast<uint32_t *>(out.getElement(0, y));
+        const uint32_t *i1 = static_cast<uint32_t *>(in.getElement(0, y*2));
+        const uint32_t *i2 = static_cast<uint32_t *>(in.getElement(0, y*2+1));
+
+        for (uint32_t x=0; x < h; x++) {
+            *oPtr = rsBoxFilter8888(i1[0], i1[1], i2[0], i2[1]);
             oPtr ++;
             i1 += 2;
             i2 += 2;
@@ -255,21 +282,25 @@
 
 static ElementConverter_t pickConverter(RsElementPredefined dstFmt, RsElementPredefined srcFmt)
 {
-    if ((dstFmt == RS_ELEMENT_RGB_565) && 
+    if ((dstFmt == RS_ELEMENT_RGB_565) &&
         (srcFmt == RS_ELEMENT_RGB_565)) {
         return elementConverter_cpy_16;
     }
 
-    if ((dstFmt == RS_ELEMENT_RGB_565) && 
+    if ((dstFmt == RS_ELEMENT_RGB_565) &&
         (srcFmt == RS_ELEMENT_RGB_888)) {
         return elementConverter_888_to_565;
     }
 
-    if ((dstFmt == RS_ELEMENT_RGB_565) && 
+    if ((dstFmt == RS_ELEMENT_RGB_565) &&
         (srcFmt == RS_ELEMENT_RGBA_8888)) {
         return elementConverter_8888_to_565;
     }
 
+    if ((dstFmt == RS_ELEMENT_RGBA_8888) &&
+        (srcFmt == RS_ELEMENT_RGBA_8888)) {
+        return elementConverter_cpy_32;
+    }
 
     LOGE("pickConverter, unsuported combo");
     return 0;
@@ -303,7 +334,7 @@
         for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) {
             adapt.setLOD(lod);
             adapt2.setLOD(lod + 1);
-            mip(adapt2, adapt);
+            mip565(adapt2, adapt);
         }
     }
 
@@ -312,6 +343,8 @@
 
 RsAllocation rsi_AllocationCreateFromFile(Context *rsc, const char *file, bool genMips)
 {
+    bool use32bpp = false;
+
     typedef struct _Win3xBitmapHeader
     {
        uint16_t type;
@@ -351,7 +384,11 @@
     int32_t texWidth = rsHigherPow2(hdr.width);
     int32_t texHeight = rsHigherPow2(hdr.height);
 
-    rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGB_565));
+    if (use32bpp) {
+        rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGBA_8888));
+    } else {
+        rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGB_565));
+    }
     rsi_TypeAdd(rsc, RS_DIMENSION_X, texWidth);
     rsi_TypeAdd(rsc, RS_DIMENSION_Y, texHeight);
     if (genMips) {
@@ -372,14 +409,29 @@
     Adapter2D adapt(texAlloc);
     uint8_t * fileInBuf = new uint8_t[texWidth * 3];
     uint32_t yOffset = (hdr.width - hdr.height) / 2;
-    uint16_t *tmp = static_cast<uint16_t *>(adapt.getElement(0, yOffset));
 
-    for (int y=0; y < hdr.height; y++) {
-        fseek(f, hdr.offset + (y*hdr.width*3), SEEK_SET);
-        fread(fileInBuf, 1, hdr.width * 3, f);
-        for(int x=0; x < hdr.width; x++) {
-            *tmp = rs888to565(fileInBuf[x*3], fileInBuf[x*3 + 1], fileInBuf[x*3 + 2]);
-            tmp++;
+    if (use32bpp) {
+        uint8_t *tmp = static_cast<uint8_t *>(adapt.getElement(0, yOffset));
+        for (int y=0; y < hdr.height; y++) {
+            fseek(f, hdr.offset + (y*hdr.width*3), SEEK_SET);
+            fread(fileInBuf, 1, hdr.width * 3, f);
+            for(int x=0; x < hdr.width; x++) {
+                tmp[0] = fileInBuf[x*3 + 2];
+                tmp[1] = fileInBuf[x*3 + 1];
+                tmp[2] = fileInBuf[x*3];
+                tmp[3] = 0xff;
+                tmp += 4;
+            }
+        }
+    } else {
+        uint16_t *tmp = static_cast<uint16_t *>(adapt.getElement(0, yOffset));
+        for (int y=0; y < hdr.height; y++) {
+            fseek(f, hdr.offset + (y*hdr.width*3), SEEK_SET);
+            fread(fileInBuf, 1, hdr.width * 3, f);
+            for(int x=0; x < hdr.width; x++) {
+                *tmp = rs888to565(fileInBuf[x*3 + 2], fileInBuf[x*3 + 1], fileInBuf[x*3]);
+                tmp++;
+            }
         }
     }
 
@@ -391,7 +443,11 @@
         for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) {
             adapt.setLOD(lod);
             adapt2.setLOD(lod + 1);
-            mip(adapt2, adapt);
+            if (use32bpp) {
+                mip8888(adapt2, adapt);
+            } else {
+                mip565(adapt2, adapt);
+            }
         }
     }
 
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index abc5f45..ec4a309 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -57,17 +57,19 @@
 
      LOGE("EGL 5");
      mContext = eglCreateContext(mDisplay, mConfig, NULL, NULL);
-     eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);   
+     eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);
      eglQuerySurface(mDisplay, mSurface, EGL_WIDTH, &mWidth);
      eglQuerySurface(mDisplay, mSurface, EGL_HEIGHT, &mHeight);
      LOGE("EGL 9");
 
 }
 
-void Context::runRootScript()
+bool Context::runRootScript()
 {
     rsAssert(mRootScript->mIsRoot);
 
+    glColor4f(1,1,1,1);
+    glEnable(GL_LIGHT0);
     glViewport(0, 0, 320, 480);
     float aspectH = 480.f / 320.f;
 
@@ -91,7 +93,7 @@
     glDepthMask(GL_TRUE);
     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
 
-    glClearColor(mRootScript->mClearColor[0], 
+    glClearColor(mRootScript->mClearColor[0],
                  mRootScript->mClearColor[1],
                  mRootScript->mClearColor[2],
                  mRootScript->mClearColor[3]);
@@ -99,8 +101,7 @@
     glClear(GL_COLOR_BUFFER_BIT);
     glClear(GL_DEPTH_BUFFER_BIT);
 
-    mRootScript->run(this, 0);
-
+    return mRootScript->run(this, 0);
 }
 
 void Context::setupCheck()
@@ -133,24 +134,19 @@
      LOGE("TP 2");
 
      rsc->mRunning = true;
+     bool mDraw = true;
      while (!rsc->mExit) {
-         gIO->playCoreCommands(rsc);
+         mDraw |= gIO->playCoreCommands(rsc);
 
-         if (!rsc->mRootScript.get()) {
+         if (!mDraw || !rsc->mRootScript.get()) {
+             usleep(10000);
              continue;
          }
 
-
-         glColor4f(1,1,1,1);
-         glEnable(GL_LIGHT0);
-
          if (rsc->mRootScript.get()) {
-             rsc->runRootScript();
+             mDraw = rsc->runRootScript();
+             eglSwapBuffers(rsc->mDisplay, rsc->mSurface);
          }
-
-         eglSwapBuffers(rsc->mDisplay, rsc->mSurface);
-
-         usleep(10000);
      }
 
      LOGE("TP 6");
@@ -158,9 +154,6 @@
      glClear(GL_COLOR_BUFFER_BIT);
      eglSwapBuffers(rsc->mDisplay, rsc->mSurface);
      eglTerminate(rsc->mDisplay);
-
-     LOGE("TP 7");
-
      return NULL;
 }
 
@@ -179,7 +172,20 @@
     gCon = this;
 
     LOGE("CC 2");
-    int status = pthread_create(&mThreadId, NULL, threadProc, this);
+    int status;
+    pthread_attr_t threadAttr;
+
+    status = pthread_attr_init(&threadAttr);
+    if (status) {
+        LOGE("Failed to init thread attribute.");
+        return;
+    }
+
+    sched_param sparam;
+    sparam.sched_priority = ANDROID_PRIORITY_DISPLAY;
+    pthread_attr_setschedparam(&threadAttr, &sparam);
+
+    status = pthread_create(&mThreadId, &threadAttr, threadProc, this);
     if (status) {
         LOGE("Failed to start rs context thread.");
     }
@@ -191,8 +197,7 @@
     }
     LOGE("CC 4");
 
-
-
+    pthread_attr_destroy(&threadAttr);
 }
 
 Context::~Context()
@@ -245,7 +250,7 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////
-// 
+//
 
 namespace android {
 namespace renderscript {
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 9d96a06..64717e4 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -108,7 +108,7 @@
 
     void initEGL();
 
-    void runRootScript();
+    bool runRootScript();
 
     static void * threadProc(void *);
 
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index bd11f72..5a44f47 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -16,6 +16,8 @@
 
 #include "rsContext.h"
 
+#include <GLES/gl.h>
+
 using namespace android;
 using namespace android::renderscript;
 
@@ -235,6 +237,108 @@
     return offset;
 }
 
+uint32_t Element::getGLType() const
+{
+    int bits[4];
+
+    if (mComponentCount > 4) {
+        return 0;
+    }
+
+    for (uint32_t ct=0; ct < mComponentCount; ct++) {
+        bits[ct] = mComponents[ct]->getBits();
+        if (mComponents[ct]->getType() != Component::UNSIGNED) {
+            return 0;
+        }
+        if (!mComponents[ct]->getIsNormalized()) {
+            return 0;
+        }
+    }
+
+    switch(mComponentCount) {
+    case 1:
+        if (bits[0] == 8) {
+            return GL_UNSIGNED_BYTE;
+        }
+        return 0;
+    case 2:
+        if ((bits[0] == 8) &&
+            (bits[1] == 8)) {
+            return GL_UNSIGNED_BYTE;
+        }
+        return 0;
+    case 3:
+        if ((bits[0] == 8) &&
+            (bits[1] == 8) &&
+            (bits[2] == 8)) {
+            return GL_UNSIGNED_BYTE;
+        }
+        if ((bits[0] == 5) &&
+            (bits[1] == 6) &&
+            (bits[2] == 5)) {
+            return GL_UNSIGNED_SHORT_5_6_5;
+        }
+        return 0;
+    case 4:
+        if ((bits[0] == 8) &&
+            (bits[1] == 8) &&
+            (bits[2] == 8) &&
+            (bits[3] == 8)) {
+            return GL_UNSIGNED_BYTE;
+        }
+        if ((bits[0] == 4) &&
+            (bits[1] == 4) &&
+            (bits[2] == 4) &&
+            (bits[3] == 4)) {
+            return GL_UNSIGNED_SHORT_4_4_4_4;
+        }
+        if ((bits[0] == 5) &&
+            (bits[1] == 5) &&
+            (bits[2] == 5) &&
+            (bits[3] == 1)) {
+            return GL_UNSIGNED_SHORT_5_5_5_1;
+        }
+    }
+    return 0;
+}
+
+uint32_t Element::getGLFormat() const
+{
+    switch(mComponentCount) {
+    case 1:
+        if (mComponents[0]->getKind() == Component::ALPHA) {
+            return GL_ALPHA;
+        }
+        if (mComponents[0]->getKind() == Component::LUMINANCE) {
+            return GL_LUMINANCE;
+        }
+        break;
+    case 2:
+        if ((mComponents[0]->getKind() == Component::LUMINANCE) &&
+            (mComponents[1]->getKind() == Component::ALPHA)) {
+            return GL_LUMINANCE_ALPHA;
+        }
+        break;
+    case 3:
+        if ((mComponents[0]->getKind() == Component::RED) &&
+            (mComponents[1]->getKind() == Component::GREEN) &&
+            (mComponents[2]->getKind() == Component::BLUE)) {
+            return GL_RGB;
+        }
+        break;
+    case 4:
+        if ((mComponents[0]->getKind() == Component::RED) &&
+            (mComponents[1]->getKind() == Component::GREEN) &&
+            (mComponents[2]->getKind() == Component::BLUE) &&
+            (mComponents[3]->getKind() == Component::ALPHA)) {
+            return GL_RGBA;
+        }
+        break;
+    }
+    return 0;
+}
+
+
 ElementState::ElementState()
 {
 }
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
index 7852ffc..2434977 100644
--- a/libs/rs/rsElement.h
+++ b/libs/rs/rsElement.h
@@ -36,6 +36,8 @@
 
     void setComponent(uint32_t idx, Component *c);
 
+    uint32_t getGLType() const;
+    uint32_t getGLFormat() const;
 
 
     size_t getSizeBits() const;
diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp
index 3f51e04..67ab434 100644
--- a/libs/rs/rsLocklessFifo.cpp
+++ b/libs/rs/rsLocklessFifo.cpp
@@ -74,6 +74,7 @@
         freeSpace = 0;
     }
     
+    //LOGE("free %i", freeSpace);
     return freeSpace;
 }
 
@@ -85,8 +86,8 @@
 
 void * LocklessCommandFifo::reserve(uint32_t sizeInBytes)
 {
-    // Add space for command header;
-    sizeInBytes += 4;
+    // Add space for command header and loop token;
+    sizeInBytes += 8;
 
     //dumpState("reserve");
     if (getFreeSpace() < sizeInBytes) {
@@ -153,16 +154,17 @@
 
 void LocklessCommandFifo::makeSpace(uint32_t bytes)
 {
+    //dumpState("make space");
     if ((mPut+bytes) > mEnd) {
         // Need to loop regardless of where get is.
-        while((mGet > mPut) && (mPut+4 >= mGet)) {
+        while((mGet > mPut) && (mBuffer+4 >= mGet)) {
             sleep(1);
         }
 
         // Toss in a reset then the normal wait for space will do the rest.
         reinterpret_cast<uint16_t *>(mPut)[0] = 0;
         reinterpret_cast<uint16_t *>(mPut)[1] = 0;
-        mPut += 4;
+        mPut = mBuffer;
     }
 
     // it will fit here so we just need to wait for space.
diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h
index 1932870..0229860 100644
--- a/libs/rs/rsScript.h
+++ b/libs/rs/rsScript.h
@@ -46,7 +46,7 @@
 
     ObjectBaseRef<Allocation> mSlots[16];
 
-    virtual void run(Context *, uint32_t launchID) = 0;
+    virtual bool run(Context *, uint32_t launchID) = 0;
 };
 
 
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index 2c7d884..36019ab 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -387,10 +387,10 @@
 };
 
 
-void ScriptC::run(Context *rsc, uint32_t launchID)
+bool ScriptC::run(Context *rsc, uint32_t launchID)
 {
     Env e = {rsc, this};
-    mScript(&e, &scriptCPtrTable, launchID);
+    return mScript(&e, &scriptCPtrTable, launchID) != 0;
 }
 
 ScriptCState::ScriptCState()
diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h
index c58dcf9..283007e 100644
--- a/libs/rs/rsScriptC.h
+++ b/libs/rs/rsScriptC.h
@@ -37,7 +37,7 @@
     virtual ~ScriptC();
 
 
-    virtual void run(Context *, uint32_t launchID);
+    virtual bool run(Context *, uint32_t launchID);
 
 
     ACCscript*    mAccScript;
diff --git a/libs/rs/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp
index d5ac70b..23c808a 100644
--- a/libs/rs/rsThreadIO.cpp
+++ b/libs/rs/rsThreadIO.cpp
@@ -34,12 +34,14 @@
 {
 }
 
-void ThreadIO::playCoreCommands(Context *con)
+bool ThreadIO::playCoreCommands(Context *con)
 {
     //LOGE("playCoreCommands 1");
     uint32_t cmdID = 0;
     uint32_t cmdSize = 0;
+    bool ret = false;
     while(!mToCore.isEmpty()) {
+        ret = true;
         //LOGE("playCoreCommands 2");
         const void * data = mToCore.get(&cmdID, &cmdSize);
         //LOGE("playCoreCommands 3 %i %i", cmdID, cmdSize);
@@ -50,6 +52,7 @@
         mToCore.next();
         //LOGE("playCoreCommands 5");
     }
+    return ret;
 }
 
 
diff --git a/libs/rs/rsThreadIO.h b/libs/rs/rsThreadIO.h
index f8ba37d..ae2ffc0 100644
--- a/libs/rs/rsThreadIO.h
+++ b/libs/rs/rsThreadIO.h
@@ -35,7 +35,9 @@
     ThreadIO();
     ~ThreadIO();
 
-    void playCoreCommands(Context *con);
+    // Plays back commands from the client.
+    // Returns true if any commands were processed.
+    bool playCoreCommands(Context *con);
 
 
     LocklessCommandFifo mToCore;
diff --git a/libs/rs/rsUtils.h b/libs/rs/rsUtils.h
index 683c96e..5a43fb3 100644
--- a/libs/rs/rsUtils.h
+++ b/libs/rs/rsUtils.h
@@ -87,22 +87,28 @@
 static inline uint16_t rs888to565(uint32_t r, uint32_t g, uint32_t b)
 {
     uint16_t t = 0;
-    t |= r >> 3;
+    t |= b >> 3;
     t |= (g >> 2) << 5;
-    t |= (b >> 3) << 11;
+    t |= (r >> 3) << 11;
     return t;
 }
 
 static inline uint16_t rsBoxFilter565(uint16_t i1, uint16_t i2, uint16_t i3, uint16_t i4)
 {
     uint32_t r = ((i1 & 0x1f) + (i2 & 0x1f) + (i3 & 0x1f) + (i4 & 0x1f));
-    uint32_t g = ((i1 >> 5) & 0x3f) + ((i2 >> 5) & 0x3f) + ((i3 >> 5) & 0x3f) + ((i1 >> 5) & 0x3f);
+    uint32_t g = ((i1 >> 5) & 0x3f) + ((i2 >> 5) & 0x3f) + ((i3 >> 5) & 0x3f) + ((i4 >> 5) & 0x3f);
     uint32_t b = ((i1 >> 11) + (i2 >> 11) + (i3 >> 11) + (i4 >> 11));
     return (r >> 2) | ((g >> 2) << 5) | ((b >> 2) << 11);
 }
 
-
-
+static inline uint32_t rsBoxFilter8888(uint32_t i1, uint32_t i2, uint32_t i3, uint32_t i4)
+{
+    uint32_t r = (i1 & 0xff) +         (i2 & 0xff) +         (i3 & 0xff) +         (i4 & 0xff);
+    uint32_t g = ((i1 >> 8) & 0xff) +  ((i2 >> 8) & 0xff) +  ((i3 >> 8) & 0xff) +  ((i4 >> 8) & 0xff);
+    uint32_t b = ((i1 >> 16) & 0xff) + ((i2 >> 16) & 0xff) + ((i3 >> 16) & 0xff) + ((i4 >> 16) & 0xff);
+    uint32_t a = ((i1 >> 24) & 0xff) + ((i2 >> 24) & 0xff) + ((i3 >> 24) & 0xff) + ((i4 >> 24) & 0xff);
+    return (r >> 2) | ((g >> 2) << 8) | ((b >> 2) << 16) | ((a >> 2) << 24);
+}
 
 
 
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 1acc1a4..3f5cb85 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -32,6 +32,7 @@
 	StopWatch.cpp \
 	String8.cpp \
 	String16.cpp \
+	StringArray.cpp \
 	SystemClock.cpp \
 	TextOutput.cpp \
 	Threads.cpp \
@@ -40,29 +41,15 @@
     ZipFileCRO.cpp \
 	ZipFileRO.cpp \
 	ZipUtils.cpp \
-	misc.cpp \
-	LogSocket.cpp
+	misc.cpp
 
-#
-# The cpp files listed here do not belong in the device
-# build.  Consult with the swetland before even thinking about
-# putting them in commonSources.
-#
-# They're used by the simulator runtime and by host-side tools like
-# aapt and the simulator front-end.
-#
-hostSources:= \
-	InetAddress.cpp \
-	Socket.cpp \
-	ZipEntry.cpp \
-	ZipFile.cpp
 
 # For the host
 # =====================================================
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:= $(commonSources) $(hostSources)
+LOCAL_SRC_FILES:= $(commonSources)
 
 ifeq ($(HOST_OS),linux)
 # Use the futex based mutex and condition variable
@@ -96,12 +83,8 @@
 LOCAL_SRC_FILES:= \
 	$(commonSources) \
 	Unicode.cpp \
-    backup_data.cpp \
-	backup_helper_file.cpp
-
-ifeq ($(TARGET_SIMULATOR),true)
-LOCAL_SRC_FILES += $(hostSources)
-endif
+    BackupData.cpp \
+	BackupHelpers.cpp
 
 ifeq ($(TARGET_OS),linux)
 # Use the futex based mutex and condition variable
@@ -129,9 +112,5 @@
 endif # sim
 
 LOCAL_MODULE:= libutils
-
-#LOCAL_CFLAGS+=
-#LOCAL_LDFLAGS:=
-
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/libs/utils/backup_data.cpp b/libs/utils/BackupData.cpp
similarity index 99%
rename from libs/utils/backup_data.cpp
rename to libs/utils/BackupData.cpp
index 95c05b7..120f23d 100644
--- a/libs/utils/backup_data.cpp
+++ b/libs/utils/BackupData.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "backup_data"
 
-#include <utils/backup_helpers.h>
+#include <utils/BackupHelpers.h>
 #include <utils/ByteOrder.h>
 
 #include <stdio.h>
diff --git a/libs/utils/backup_helper_file.cpp b/libs/utils/BackupHelpers.cpp
similarity index 99%
rename from libs/utils/backup_helper_file.cpp
rename to libs/utils/BackupHelpers.cpp
index 7ec2ce8..7f423a8 100644
--- a/libs/utils/backup_helper_file.cpp
+++ b/libs/utils/BackupHelpers.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "file_backup_helper"
 
-#include <utils/backup_helpers.h>
+#include <utils/BackupHelpers.h>
 
 #include <utils/KeyedVector.h>
 #include <utils/ByteOrder.h>
@@ -350,10 +350,11 @@
             FileState& g = newSnapshot.editValueAt(m);
 
             int fd = open(realFilename.string(), O_RDONLY);
-            if (fd != -1) {
+            if (fd < 0) {
                 // We can't open the file.  Don't report it as a delete either.  Let the
                 // server keep the old version.  Maybe they'll be able to deal with it
                 // on restore.
+                LOGP("Unable to open file %s - skipping", realFilename.string());
             } else {
                 g.crc32 = compute_crc32(fd);
 
diff --git a/libs/utils/characterData.h b/libs/utils/CharacterData.h
similarity index 100%
rename from libs/utils/characterData.h
rename to libs/utils/CharacterData.h
diff --git a/libs/utils/InetAddress.cpp b/libs/utils/InetAddress.cpp
deleted file mode 100644
index 39a0a68..0000000
--- a/libs/utils/InetAddress.cpp
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Internet address class.
-//
-#ifdef HAVE_WINSOCK
-# include <winsock2.h>
-#else
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-//# include <arpa/inet.h>
-# include <netdb.h>
-#endif
-
-#include <utils/inet_address.h>
-#include <utils/threads.h>
-#include <utils/Log.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-using namespace android;
-
-
-/*
- * ===========================================================================
- *      InetAddress
- * ===========================================================================
- */
-
-// lock for the next couple of functions; could tuck into InetAddress
-static Mutex*   gGHBNLock;
-
-/*
- * Lock/unlock access to the hostent struct returned by gethostbyname().
- */
-static inline void lock_gethostbyname(void)
-{
-    if (gGHBNLock == NULL)
-        gGHBNLock = new Mutex;
-    gGHBNLock->lock();
-}
-static inline void unlock_gethostbyname(void)
-{
-    assert(gGHBNLock != NULL);
-    gGHBNLock->unlock();
-}
-
-
-/*
- * Constructor -- just init members.  This is private so that callers
- * are required to use getByName().
- */
-InetAddress::InetAddress(void)
-    : mAddress(NULL), mLength(-1), mName(NULL)
-{
-}
-
-/*
- * Destructor -- free address storage.
- */
-InetAddress::~InetAddress(void)
-{
-    delete[] (char*) mAddress;
-    delete[] mName;
-}
-
-/*
- * Copy constructor.
- */
-InetAddress::InetAddress(const InetAddress& orig)
-{
-    *this = orig;   // use assignment code
-}
-
-/*
- * Assignment operator.
- */
-InetAddress& InetAddress::operator=(const InetAddress& addr)
-{
-    // handle self-assignment
-    if (this == &addr)
-        return *this;
-    // copy mLength and mAddress
-    mLength = addr.mLength;
-    if (mLength > 0) {
-        mAddress = new char[mLength];
-        memcpy(mAddress, addr.mAddress, mLength);
-        LOG(LOG_DEBUG, "socket",
-            "HEY: copied %d bytes in assignment operator\n", mLength);
-    } else {
-        mAddress = NULL;
-    }
-    // copy mName
-    mName = new char[strlen(addr.mName)+1];
-    strcpy(mName, addr.mName);
-
-    return *this;
-}
-
-/*
- * Create a new object from a name or a dotted-number IP notation.
- *
- * Returns NULL on failure.
- */
-InetAddress*
-InetAddress::getByName(const char* host)
-{
-    InetAddress* newAddr = NULL;
-    struct sockaddr_in addr;
-    struct hostent* he;
-    DurationTimer hostTimer, lockTimer;
-
-    // gethostbyname() isn't reentrant, so we need to lock things until
-    // we can copy the data out.
-    lockTimer.start();
-    lock_gethostbyname();
-    hostTimer.start();
-
-    he = gethostbyname(host);
-    if (he == NULL) {
-        LOG(LOG_WARN, "socket", "WARNING: cannot resolve host %s\n", host);
-        unlock_gethostbyname();
-        return NULL;
-    }
-
-    memcpy(&addr.sin_addr, he->h_addr, he->h_length);
-    addr.sin_family = he->h_addrtype;
-    addr.sin_port = 0;
-
-    // got it, unlock us
-    hostTimer.stop();
-    he = NULL;
-    unlock_gethostbyname();
-
-    lockTimer.stop();
-    if ((long) lockTimer.durationUsecs() > 100000) {
-        long lockTime = (long) lockTimer.durationUsecs();
-        long hostTime = (long) hostTimer.durationUsecs();
-        LOG(LOG_DEBUG, "socket",
-            "Lookup of %s took %.3fs (gethostbyname=%.3fs lock=%.3fs)\n",
-            host, lockTime / 1000000.0, hostTime / 1000000.0,
-            (lockTime - hostTime) / 1000000.0);
-    }
-
-    // Alloc storage and copy it over.
-    newAddr = new InetAddress();
-    if (newAddr == NULL)
-        return NULL;
-
-    newAddr->mLength = sizeof(struct sockaddr_in);
-    newAddr->mAddress = new char[sizeof(struct sockaddr_in)];
-    if (newAddr->mAddress == NULL) {
-        delete newAddr;
-        return NULL;
-    }
-    memcpy(newAddr->mAddress, &addr, newAddr->mLength);
-
-    // Keep this for debug messages.
-    newAddr->mName = new char[strlen(host)+1];
-    if (newAddr->mName == NULL) {
-        delete newAddr;
-        return NULL;
-    }
-    strcpy(newAddr->mName, host);
-
-    return newAddr;
-}
-
-
-/*
- * ===========================================================================
- *      InetSocketAddress
- * ===========================================================================
- */
-
-/*
- * Create an address with the host wildcard (INADDR_ANY).
- */
-bool InetSocketAddress::create(int port)
-{
-    assert(mAddress == NULL);
-
-    mAddress = InetAddress::getByName("0.0.0.0");
-    if (mAddress == NULL)
-        return false;
-    mPort = port;
-    return true;
-}
-
-/*
- * Create address with host and port specified.
- */
-bool InetSocketAddress::create(const InetAddress* addr, int port)
-{
-    assert(mAddress == NULL);
-
-    mAddress = new InetAddress(*addr);  // make a copy
-    if (mAddress == NULL)
-        return false;
-    mPort = port;
-    return true;
-}
-
-/*
- * Create address with host and port specified.
- */
-bool InetSocketAddress::create(const char* host, int port)
-{
-    assert(mAddress == NULL);
-
-    mAddress = InetAddress::getByName(host);
-    if (mAddress == NULL)
-        return false;
-    mPort = port;
-    return true;
-}
-
diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp
deleted file mode 100644
index 55c1b99..0000000
--- a/libs/utils/LogSocket.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef HAVE_WINSOCK
-//#define SOCKETLOG
-#endif
-
-#ifdef SOCKETLOG
-
-#define LOG_TAG "SOCKETLOG"
-
-#include <string.h>
-#include <cutils/log.h>
-#include "utils/LogSocket.h"
-#include "utils/logger.h"
-#include "cutils/hashmap.h"
-
-// defined in //device/data/etc/event-log-tags
-#define SOCKET_CLOSE_LOG 51000
-
-static Hashmap* statsMap = NULL;
-
-#define LOG_LIST_NUMBER 5
-
-typedef struct SocketStats {
-    int fd;
-    unsigned int send;
-    unsigned int recv;
-    unsigned int ip;
-    unsigned short port;
-    short reason;
-}SocketStats;
-
-SocketStats *get_socket_stats(int fd) {
-    if (statsMap == NULL) {
-        statsMap = hashmapCreate(8, &hashmapIntHash, &hashmapIntEquals);
-    }
-
-    SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd);
-    if (s == NULL) {
-        // LOGD("create SocketStats for fd %d", fd);
-        s = (SocketStats*) malloc(sizeof(SocketStats));
-        memset(s, 0, sizeof(SocketStats));
-        s->fd = fd;
-        hashmapPut(statsMap, &s->fd, s);
-    }
-    return s;
-}
-
-void log_socket_connect(int fd, unsigned int ip, unsigned short port) {
-    // LOGD("log_socket_connect for fd %d ip %d port%d", fd, ip, port);
-    SocketStats *s = get_socket_stats(fd);
-    s->ip = ip;
-    s->port = port;
-}
-
-void add_send_stats(int fd, int send) {
-    if (send <=0) {
-        LOGE("add_send_stats send %d", send);
-        return;
-    }
-    SocketStats *s = get_socket_stats(fd);
-    s->send += send;
-    // LOGD("add_send_stats for fd %d ip %d port%d", fd, s->ip, s->port);
-}
-
-void add_recv_stats(int fd, int recv) {
-    if (recv <=0) {
-        LOGE("add_recv_stats recv %d", recv);
-        return;
-    }
-    SocketStats *s = get_socket_stats(fd);
-    s->recv += recv;
-    // LOGD("add_recv_stats for fd %d ip %d port%d", fd, s->ip, s->port);
-}
-
-char* put_int(char* buf, int value) {
-    *buf = EVENT_TYPE_INT;
-    buf++;
-    memcpy(buf, &value, sizeof(int));
-    return buf + sizeof(int);
-}
-
-void log_socket_close(int fd, short reason) {
-    if (statsMap) {
-        SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd);
-        if (s != NULL) {
-            if (s->send != 0 || s->recv != 0) {
-                s->reason = reason;
-                // 5 int + list type need 2 bytes
-                char buf[LOG_LIST_NUMBER * 5 + 2];
-                buf[0] = EVENT_TYPE_LIST;
-                buf[1] = LOG_LIST_NUMBER;
-                char* writePos = buf + 2;
-                writePos = put_int(writePos, s->send);
-                writePos = put_int(writePos, s->recv);
-                writePos = put_int(writePos, s->ip);
-                writePos = put_int(writePos, s->port);
-                writePos = put_int(writePos, s->reason);
-                
-                android_bWriteLog(SOCKET_CLOSE_LOG, buf, sizeof(buf));
-                // LOGD("send %d recv %d reason %d", s->send, s->recv, s->reason);
-            }
-            hashmapRemove(statsMap, &s->fd);
-            free(s);
-        }
-    }
-}
-
-#else
-void add_send_stats(int fd, int send) {} 
-void add_recv_stats(int fd, int recv) {}
-void log_socket_close(int fd, short reason) {}
-void log_socket_connect(int fd, unsigned int ip, unsigned short port) {}
-#endif
diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp
deleted file mode 100644
index 51509a3..0000000
--- a/libs/utils/Socket.cpp
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Internet address class.
-//
-
-#ifdef HAVE_WINSOCK
-// This needs to come first, or Cygwin gets concerned about a potential
-// clash between WinSock and <sys/types.h>.
-# include <winsock2.h>
-#endif
-
-#include <utils/Socket.h>
-#include <utils/inet_address.h>
-#include <utils/Log.h>
-#include <utils/Timers.h>
-
-#ifndef HAVE_WINSOCK
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-# include <arpa/inet.h>
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-
-using namespace android;
-
-
-/*
- * ===========================================================================
- *      Socket
- * ===========================================================================
- */
-
-#ifndef INVALID_SOCKET
-# define INVALID_SOCKET (-1)
-#endif
-#define UNDEF_SOCKET   ((unsigned long) INVALID_SOCKET)
-
-/*static*/ bool Socket::mBootInitialized = false;
-
-/*
- * Extract system-dependent error code.
- */
-static inline int getSocketError(void) {
-#ifdef HAVE_WINSOCK
-    return WSAGetLastError();
-#else
-    return errno;
-#endif
-}
-
-/*
- * One-time initialization for socket code.
- */
-/*static*/ bool Socket::bootInit(void)
-{
-#ifdef HAVE_WINSOCK
-    WSADATA wsaData;
-    int err;
-
-    err = WSAStartup(MAKEWORD(2, 0), &wsaData);
-    if (err != 0) {
-        LOG(LOG_ERROR, "socket", "Unable to start WinSock\n");
-        return false;
-    }
-
-    LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n",
-        LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
-#endif
-
-    mBootInitialized = true;
-    return true;
-}
-
-/*
- * One-time shutdown for socket code.
- */
-/*static*/ void Socket::finalShutdown(void)
-{
-#ifdef HAVE_WINSOCK
-    WSACleanup();
-#endif
-    mBootInitialized = false;
-}
-
-
-/*
- * Simple constructor.  Allow the application to create us and then make
- * bind/connect calls.
- */
-Socket::Socket(void)
-    : mSock(UNDEF_SOCKET)
-{
-    if (!mBootInitialized)
-        LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n");
-}
-
-/*
- * Destructor.  Closes the socket and resets our storage.
- */
-Socket::~Socket(void)
-{
-    close();
-}
-
-
-/*
- * Create a socket and connect to the specified host and port.
- */
-int Socket::connect(const char* host, int port)
-{
-    if (mSock != UNDEF_SOCKET) {
-        LOG(LOG_WARN, "socket", "Socket already connected\n");
-        return -1;
-    }
-
-    InetSocketAddress sockAddr;
-    if (!sockAddr.create(host, port))
-        return -1;
-
-    //return doConnect(sockAddr);
-    int foo;
-    foo = doConnect(sockAddr);
-    return foo;
-}
-
-/*
- * Create a socket and connect to the specified host and port.
- */
-int Socket::connect(const InetAddress* addr, int port)
-{
-    if (mSock != UNDEF_SOCKET) {
-        LOG(LOG_WARN, "socket", "Socket already connected\n");
-        return -1;
-    }
-
-    InetSocketAddress sockAddr;
-    if (!sockAddr.create(addr, port))
-        return -1;
-
-    return doConnect(sockAddr);
-}
-
-/*
- * Finish creating a socket by connecting to the remote host.
- *
- * Returns 0 on success.
- */
-int Socket::doConnect(const InetSocketAddress& sockAddr)
-{
-#ifdef HAVE_WINSOCK
-    SOCKET sock;
-#else
-    int sock;
-#endif
-    const InetAddress* addr = sockAddr.getAddress();
-    int port = sockAddr.getPort();
-    struct sockaddr_in inaddr;
-    DurationTimer connectTimer;
-
-    assert(sizeof(struct sockaddr_in) == addr->getAddressLength());
-    memcpy(&inaddr, addr->getAddress(), addr->getAddressLength());
-    inaddr.sin_port = htons(port);
-
-    //fprintf(stderr, "--- connecting to %s:%d\n",
-    //    sockAddr.getHostName(), port);
-
-    sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
-    if (sock == INVALID_SOCKET) {
-        int err = getSocketError();
-        LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err);
-        return (err != 0) ? err : -1;
-    }
-
-    connectTimer.start();
-
-    if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) {
-        int err = getSocketError();
-        LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n",
-            sockAddr.getHostName(), port, err);
-        return (err != 0) ? err : -1;
-    }
-
-    connectTimer.stop();
-    if ((long) connectTimer.durationUsecs() > 100000) {
-        LOG(LOG_INFO, "socket",
-            "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(),
-            port, ((long) connectTimer.durationUsecs()) / 1000000.0);
-    }
-
-    mSock = (unsigned long) sock;
-    LOG(LOG_VERBOSE, "socket",
-        "--- connected to %s:%d\n", sockAddr.getHostName(), port);
-    return 0;
-}
-
-
-/*
- * Close the socket if it needs closing.
- */
-bool Socket::close(void)
-{
-    if (mSock != UNDEF_SOCKET) {
-        //fprintf(stderr, "--- closing socket %lu\n", mSock);
-#ifdef HAVE_WINSOCK
-        if (::closesocket((SOCKET) mSock) != 0)
-            return false;
-#else
-        if (::close((int) mSock) != 0)
-            return false;
-#endif
-    }
-
-    mSock = UNDEF_SOCKET;
-
-    return true;
-}
-
-/*
- * Read data from socket.
- *
- * Standard semantics: read up to "len" bytes into "buf".  Returns the
- * number of bytes read, or less than zero on error.
- */
-int Socket::read(void* buf, ssize_t len) const
-{
-    if (mSock == UNDEF_SOCKET) {
-        LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n");
-        return -500;
-    }
-
-#ifdef HAVE_WINSOCK
-    SOCKET sock = (SOCKET) mSock;
-#else
-    int sock = (int) mSock;
-#endif
-    int cc;
-
-    cc = recv(sock, (char*)buf, len, 0);
-    if (cc < 0) {
-        int err = getSocketError();
-        return (err > 0) ? -err : -1;
-    }
-
-    return cc;
-}
-
-/*
- * Write data to a socket.
- *
- * Standard semantics: write up to "len" bytes into "buf".  Returns the
- * number of bytes written, or less than zero on error.
- */
-int Socket::write(const void* buf, ssize_t len) const
-{
-    if (mSock == UNDEF_SOCKET) {
-        LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n");
-        return -500;
-    }
-
-#ifdef HAVE_WINSOCK
-    SOCKET sock = (SOCKET) mSock;
-#else
-    int sock = (int) mSock;
-#endif
-    int cc;
-
-    cc = send(sock, (const char*)buf, len, 0);
-    if (cc < 0) {
-        int err = getSocketError();
-        return (err > 0) ? -err : -1;
-    }
-
-    return cc;
-}
-
-
-/*
- * ===========================================================================
- *      Socket tests
- * ===========================================================================
- */
-
-/*
- * Read all data from the socket.  The data is read into a buffer that
- * expands as needed.
- *
- * On exit, the buffer is returned, and the length of the data is stored
- * in "*sz".  A null byte is added to the end, but is not included in
- * the length.
- */
-static char* socketReadAll(const Socket& s, int *sz)
-{
-    int max, r;
-    char *data, *ptr, *tmp;
-
-    data = (char*) malloc(max = 32768);
-    if (data == NULL)
-        return NULL;
-
-    ptr = data;
-    
-    for (;;) {
-        if ((ptr - data) == max) {
-            tmp = (char*) realloc(data, max *= 2);
-            if(tmp == 0) {
-                free(data);
-                return 0;
-            }
-        }
-        r = s.read(ptr, max - (ptr - data));
-        if (r == 0)
-            break;
-        if (r < 0) {
-            LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r);
-            break;
-        }
-        ptr += r;
-    }
-
-    if ((ptr - data) == max) {
-        tmp = (char*) realloc(data, max + 1);
-        if (tmp == NULL) {
-            free(data);
-            return NULL;
-        }
-    }
-    *ptr = '\0';
-    *sz = (ptr - data);
-    return data;
-}
-
-/*
- * Exercise the Socket class.
- */
-void android::TestSockets(void)
-{
-    printf("----- SOCKET TEST ------\n");
-    Socket::bootInit();
-
-    char* buf = NULL;
-    int len, cc;
-    const char* kTestStr =
-        "GET / HTTP/1.0\n"
-        "Connection: close\n"
-        "\n";
-
-    Socket sock;
-    if (sock.connect("www.google.com", 80) != 0) {
-        fprintf(stderr, "socket connected failed\n");
-        goto bail;
-    }
-
-    cc = sock.write(kTestStr, strlen(kTestStr));
-    if (cc != (int) strlen(kTestStr)) {
-        fprintf(stderr, "write failed, res=%d\n", cc);
-        goto bail;
-    }
-    buf = socketReadAll(sock, &len);
-
-    printf("GOT '%s'\n", buf);
-
-bail:
-    sock.close();
-    free(buf);
-}
-
diff --git a/libs/utils/StringArray.cpp b/libs/utils/StringArray.cpp
new file mode 100644
index 0000000..aa42d68
--- /dev/null
+++ b/libs/utils/StringArray.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+//
+// Sortable array of strings.  STL-ish, but STL-free.
+//  
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/StringArray.h>
+
+namespace android {
+
+//
+// An expanding array of strings.  Add, get, sort, delete.
+//
+StringArray::StringArray()
+    : mMax(0), mCurrent(0), mArray(NULL)
+{
+}
+
+StringArray:: ~StringArray() {
+    for (int i = 0; i < mCurrent; i++)
+        delete[] mArray[i];
+    delete[] mArray;
+}
+
+//
+// Add a string.  A copy of the string is made.
+//
+bool StringArray::push_back(const char* str) {
+    if (mCurrent >= mMax) {
+        char** tmp;
+
+        if (mMax == 0)
+            mMax = 16;      // initial storage
+        else
+            mMax *= 2;
+
+        tmp = new char*[mMax];
+        if (tmp == NULL)
+            return false;
+
+        memcpy(tmp, mArray, mCurrent * sizeof(char*));
+        delete[] mArray;
+        mArray = tmp;
+    }
+
+    int len = strlen(str);
+    mArray[mCurrent] = new char[len+1];
+    memcpy(mArray[mCurrent], str, len+1);
+    mCurrent++;
+
+    return true;
+}
+
+//
+// Delete an entry.
+//
+void StringArray::erase(int idx) {
+    if (idx < 0 || idx >= mCurrent)
+        return;
+    delete[] mArray[idx];
+    if (idx < mCurrent-1) {
+        memmove(&mArray[idx], &mArray[idx+1],
+                (mCurrent-1 - idx) * sizeof(char*));
+    }
+    mCurrent--;
+}
+
+//
+// Sort the array.
+//
+void StringArray::sort(int (*compare)(const void*, const void*)) {
+    qsort(mArray, mCurrent, sizeof(char*), compare);
+}
+
+//
+// Pass this to the sort routine to do an ascending alphabetical sort.
+//
+int StringArray::cmpAscendingAlpha(const void* pstr1, const void* pstr2) {
+    return strcmp(*(const char**)pstr1, *(const char**)pstr2);
+}
+
+//
+// Set entry N to specified string.
+// [should use operator[] here]
+//
+void StringArray::setEntry(int idx, const char* str) {
+    if (idx < 0 || idx >= mCurrent)
+        return;
+    delete[] mArray[idx];
+    int len = strlen(str);
+    mArray[idx] = new char[len+1];
+    memcpy(mArray[idx], str, len+1);
+}
+
+
+}; // namespace android
diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp
index 33f535f..f92703e 100644
--- a/libs/utils/Unicode.cpp
+++ b/libs/utils/Unicode.cpp
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#include "utils/AndroidUnicode.h"
-#include "characterData.h"
+#include <utils/AndroidUnicode.h>
+#include "CharacterData.h"
 
 #define LOG_TAG "Unicode"
-#include "utils/Log.h"
+#include <utils/Log.h>
 
 // ICU headers for using macros
 #include <unicode/utf16.h>
diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp
index d312daf..45f6c8b 100644
--- a/libs/utils/ZipFileCRO.cpp
+++ b/libs/utils/ZipFileCRO.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include "utils/ZipFileCRO.h"
-#include "utils/ZipFileRO.h"
+#include <utils/ZipFileCRO.h>
+#include <utils/ZipFileRO.h>
 
 using namespace android;
 
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index ae8c719..6c701dd 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -19,9 +19,9 @@
 //
 #define LOG_TAG "zipro"
 //#define LOG_NDEBUG 0
-#include "utils/ZipFileRO.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
+#include <utils/ZipFileRO.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
 
 #include <zlib.h>
 
diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp
index bfbacfe..5df94cb 100644
--- a/libs/utils/ZipUtils.cpp
+++ b/libs/utils/ZipUtils.cpp
@@ -20,9 +20,9 @@
 
 #define LOG_TAG "ziputil"
 
-#include "utils/ZipUtils.h"
-#include "utils/ZipFileRO.h"
-#include "utils/Log.h"
+#include <utils/ZipUtils.h>
+#include <utils/ZipFileRO.h>
+#include <utils/Log.h>
 
 #include <stdlib.h>
 #include <string.h>
diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp
index 6d2cc91..cffe880 100644
--- a/opengl/libagl/array.cpp
+++ b/opengl/libagl/array.cpp
@@ -960,6 +960,8 @@
     v->index = first;
     first &= vertex_cache_t::INDEX_MASK;
     const GLubyte* vp = c->arrays.vertex.element(first);
+    v->obj.z = 0;
+    v->obj.w = 0x10000;
     c->arrays.vertex.fetch(c, v->obj.v, vp);
     c->arrays.mvp_transform(&c->transforms.mvp, &v->clip, &v->obj);
     c->arrays.perspective(c, v);
@@ -975,6 +977,8 @@
     do {
         v->flags = 0;
         v->index = first++;
+        v->obj.z = 0;
+        v->obj.w = 0x10000;
         c->arrays.vertex.fetch(c, v->obj.v, vp);
         c->arrays.mvp_transform(mvp, &v->clip, &v->obj);
         c->arrays.perspective(c, v);
diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp
index 25c41d0..8ae32cc0f 100644
--- a/opengl/libagl/light.cpp
+++ b/opengl/libagl/light.cpp
@@ -38,13 +38,14 @@
 static void lightVertexMaterial(ogles_context_t* c, vertex_t* v);
 
 static inline void vscale3(GLfixed* d, const GLfixed* m, GLfixed s);
-static inline void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b);
 
 static __attribute__((noinline))
 void vnorm3(GLfixed* d, const GLfixed* a);
 
 static inline void vsa3(GLfixed* d,
     const GLfixed* m, GLfixed s, const GLfixed* a);
+static inline void vss3(GLfixed* d,
+    const GLfixed* m, GLfixed s, const GLfixed* a);
 static inline void vmla3(GLfixed* d,
     const GLfixed* m0, const GLfixed* m1, const GLfixed* a);
 static inline void vmul3(GLfixed* d,
@@ -151,18 +152,10 @@
 }
 
 static inline
-void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b) {
-    const GLfixed wa = a[3];
-    const GLfixed wb = b[3];
-    if (ggl_likely(wa == wb)) {
-        d[0] = a[0] - b[0];
-        d[1] = a[1] - b[1];
-        d[2] = a[2] - b[2];
-    } else {
-        d[0] = gglMulSubx(a[0], wb, gglMulx(b[0], wa));
-        d[1] = gglMulSubx(a[1], wb, gglMulx(b[1], wa));
-        d[2] = gglMulSubx(a[2], wb, gglMulx(b[2], wa));
-    }
+void vss3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) {
+    d[0] = gglMulSubx(m[0], s, a[0]);
+    d[1] = gglMulSubx(m[1], s, a[1]);
+    d[2] = gglMulSubx(m[2], s, a[2]);
 }
 
 static inline
@@ -227,7 +220,7 @@
         const int i = 31 - gglClz(en);
         en &= ~(1<<i);
         light_t& l = c->lighting.lights[i];
-        c->transforms.mvui.point3(&c->transforms.mvui,
+        c->transforms.mvui.point4(&c->transforms.mvui,
                 &l.objPosition, &l.position);
         vnorm3(l.normalizedObjPosition.v, l.objPosition.v);
     }
@@ -318,6 +311,11 @@
         vmul3(l.implicitAmbient.v,  material.ambient.v,  l.ambient.v);
         vmul3(l.implicitDiffuse.v,  material.diffuse.v,  l.diffuse.v);
         vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v);
+        // this is just a flag to tell if we have a specular component
+        l.implicitSpecular.v[3] =
+                l.implicitSpecular.r |
+                l.implicitSpecular.g |
+                l.implicitSpecular.b;
     }
     // emission and ambient for the whole scene
     vmla3(  c->lighting.implicitSceneEmissionAndAmbient.v,
@@ -343,7 +341,11 @@
         vec4_t n;
         c->arrays.normal.fetch(c, n.v,
             c->arrays.normal.element(v->index & vertex_cache_t::INDEX_MASK));
-        if (c->transforms.rescaleNormals == GL_NORMALIZE)
+
+        // TODO: right now we handle GL_RESCALE_NORMALS as if ti were
+        // GL_NORMALIZE. We could optimize this by  scaling mvui 
+        // appropriately instead.
+        if (c->transforms.rescaleNormals)
             vnorm3(n.v, n.v);
 
         const material_t& material = c->lighting.front;
@@ -360,7 +362,8 @@
 
             // compute vertex-to-light vector
             if (ggl_unlikely(l.position.w)) {
-                vsub3w(d.v, l.objPosition.v, v->obj.v);
+                // lightPos/1.0 - vertex/vertex.w == lightPos*vertex.w - vertex
+                vss3(d.v, l.objPosition.v, v->obj.w, v->obj.v);
                 sqDist = dot3(d.v, d.v);
                 vscale3(d.v, d.v, gglSqrtRecipx(sqDist));
             } else {
diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp
index f175cda..0b68dc0 100644
--- a/opengl/libagl/matrix.cpp
+++ b/opengl/libagl/matrix.cpp
@@ -55,7 +55,7 @@
 static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o);
 static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o);
 static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o);
-static void normal__generic(transform_t const*, vec4_t* c, vec4_t const* o);
+static void point4__mvui(transform_t const*, vec4_t* c, vec4_t const* o);
 
 // ----------------------------------------------------------------------------
 #if 0
@@ -209,7 +209,8 @@
 {
     flags = 0;
     ops = OP_ALL;
-    point3 = normal__generic;
+    point3 = point4__mvui;
+    point4 = point4__mvui;
 }
 
 void transform_t::dump(const char* what)
@@ -596,66 +597,19 @@
 
 void transform_state_t::update_mvui()
 {
+    GLfloat r[16];
     const GLfloat* const mv = modelview.top().elements();
-
-    /*
-    When transforming normals, we can use the upper 3x3 matrix, see:
-    http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node26.html
-    */
     
-    // Also note that:
-    //      l(obj) =  tr(M).l(eye) for infinite light
-    //      l(obj) = inv(M).l(eye) for local light
-
-    const uint32_t ops = modelview.top_ops() & ~OP_TRANSLATE;
-    if (ggl_likely((!(ops & ~OP_ROTATE)) ||
-        (rescaleNormals && modelview.isRigidBody()))) {
-        // if the modelview matrix is a rigid body transformation
-        // (translation, rotation, uniform scaling), then we can bypass
-        // the inverse by transposing the matrix.
-        GLfloat rescale = 1.0f;
-        if (rescaleNormals == GL_RESCALE_NORMAL) {
-            if (!(ops & ~OP_UNIFORM_SCALE)) {
-                rescale = reciprocalf(mv[I(0,0)]);
-            } else {
-                rescale = rsqrtf(
-                        sqrf(mv[I(2,0)]) + sqrf(mv[I(2,1)]) + sqrf(mv[I(2,2)]));
-            }
-        }
-        GLfixed* const x = mvui.matrix.m;
-        for (int i=0 ; i<3 ; i++) {
-            x[I(i,0)] = gglFloatToFixed(mv[I(0,i)] * rescale);
-            x[I(i,1)] = gglFloatToFixed(mv[I(1,i)] * rescale);
-            x[I(i,2)] = gglFloatToFixed(mv[I(2,i)] * rescale);
-        }
-        mvui.picker();
-        return;
-    }
-
-    GLfloat r[3][3];
-    r[0][0] = det22(mv[I(1,1)], mv[I(2,1)], mv[I(1,2)], mv[I(2,2)]);
-    r[0][1] =ndet22(mv[I(0,1)], mv[I(2,1)], mv[I(0,2)], mv[I(2,2)]);
-    r[0][2] = det22(mv[I(0,1)], mv[I(1,1)], mv[I(0,2)], mv[I(1,2)]);
-    r[1][0] =ndet22(mv[I(1,0)], mv[I(2,0)], mv[I(1,2)], mv[I(2,2)]);
-    r[1][1] = det22(mv[I(0,0)], mv[I(2,0)], mv[I(0,2)], mv[I(2,2)]);
-    r[1][2] =ndet22(mv[I(0,0)], mv[I(1,0)], mv[I(0,2)], mv[I(1,2)]);
-    r[2][0] = det22(mv[I(1,0)], mv[I(2,0)], mv[I(1,1)], mv[I(2,1)]);
-    r[2][1] =ndet22(mv[I(0,0)], mv[I(2,0)], mv[I(0,1)], mv[I(2,1)]);
-    r[2][2] = det22(mv[I(0,0)], mv[I(1,0)], mv[I(0,1)], mv[I(1,1)]);        
-
-    GLfloat rdet;
-    if (rescaleNormals == GL_RESCALE_NORMAL) {
-        rdet = rsqrtf(sqrf(r[0][2]) + sqrf(r[1][2]) + sqrf(r[2][2]));
-    } else {
-        rdet = reciprocalf( 
-            r[0][0]*mv[I(0,0)] + r[0][1]*mv[I(1,0)] + r[0][2]*mv[I(2,0)]);
-    }
+    // TODO: we need a faster invert, especially for when the modelview
+    // is a rigid-body matrix
+    invert(r, mv);
 
     GLfixed* const x = mvui.matrix.m;
-    for (int i=0 ; i<3 ; i++) {
-        x[I(i,0)] = gglFloatToFixed(r[i][0] * rdet);
-        x[I(i,1)] = gglFloatToFixed(r[i][1] * rdet);
-        x[I(i,2)] = gglFloatToFixed(r[i][2] * rdet);
+    for (int i=0 ; i<4 ; i++) {
+        x[I(i,0)] = gglFloatToFixed(r[I(i,0)]);
+        x[I(i,1)] = gglFloatToFixed(r[I(i,1)]);
+        x[I(i,2)] = gglFloatToFixed(r[I(i,2)]);
+        x[I(i,4)] = gglFloatToFixed(r[I(i,3)]);
     }
     mvui.picker();
 }
@@ -783,14 +737,19 @@
     lhs->w = mla4(rx, m[ 3], ry, m[ 7], rz, m[11], rw, m[15]);
 }
 
-void normal__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+void point4__mvui(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+    // this used for transforming light positions back to object space.
+    // Lights have 3 components positions, so w is always 1.
+    // however, it is used as a switch for directional lights, so we need
+    // to preserve it.
     const GLfixed* const m = mx->matrix.m;
     const GLfixed rx = rhs->x;
     const GLfixed ry = rhs->y;
     const GLfixed rz = rhs->z;
-    lhs->x = mla3(rx, m[ 0], ry, m[ 4], rz, m[ 8]); 
-    lhs->y = mla3(rx, m[ 1], ry, m[ 5], rz, m[ 9]);
-    lhs->z = mla3(rx, m[ 2], ry, m[ 6], rz, m[10]);
+    lhs->x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); 
+    lhs->y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]);
+    lhs->z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]);
+    lhs->w = rhs->w;
 }
 
 
diff --git a/opengl/tests/lighting1709/Android.mk b/opengl/tests/lighting1709/Android.mk
new file mode 100644
index 0000000..9563e61
--- /dev/null
+++ b/opengl/tests/lighting1709/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := LightingTest
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/opengl/tests/lighting1709/AndroidManifest.xml b/opengl/tests/lighting1709/AndroidManifest.xml
new file mode 100644
index 0000000..6c23d42
--- /dev/null
+++ b/opengl/tests/lighting1709/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.lightingtest">
+
+    <application>
+        <activity android:name="ClearActivity" android:label="LightingTest">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java b/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java
new file mode 100644
index 0000000..3ae8c5c
--- /dev/null
+++ b/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java
@@ -0,0 +1,174 @@
+/*
+ * 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.lightingtest;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.app.Activity;
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.MotionEvent;
+
+public class ClearActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mGLView = new ClearGLSurfaceView(this);
+        setContentView(mGLView);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mGLView.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mGLView.onResume();
+    }
+    private GLSurfaceView mGLView;
+}
+
+class ClearGLSurfaceView extends GLSurfaceView {
+    public ClearGLSurfaceView(Context context) {
+        super(context);
+        mRenderer = new ClearRenderer();
+        setRenderer(mRenderer);
+    }
+
+    ClearRenderer mRenderer;
+}
+
+class ClearRenderer implements GLSurfaceView.Renderer {
+    public ClearRenderer() {
+    }
+
+    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        // Do nothing special.
+    }
+
+    public void onSurfaceChanged(GL10 gl, int w, int h) {
+        // Compute the projection matrix
+        gl.glMatrixMode(GL10.GL_PROJECTION);
+        gl.glLoadIdentity();
+
+        // Compute the boundaries of the frustum
+        float fl = (float) (-(w / 2)) / 288;
+        float fr = (float) (w / 2) / 288;
+        float ft = (float) (h / 2) / 288;
+        float fb = (float) (-(h / 2)) / 288;
+
+        // Set the view frustum
+        gl.glFrustumf(fl, fr, fb, ft, 1.0f, 2000.0f);
+
+        // Set the viewport dimensions
+        gl.glMatrixMode(GL10.GL_MODELVIEW);
+        gl.glLoadIdentity();
+        gl.glViewport(0, 0, w, h);
+    }
+
+    public void onDrawFrame(GL10 gl) {
+        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+
+        final float lightOff[]        = {0.0f, 0.0f,  0.0f, 1.0f};
+        final float lightAmbient[]    = {5.0f, 0.0f,  0.0f, 1.0f};
+        final float lightDiffuse[]    = {0.0f, 2.0f,  0.0f, 0.0f};
+        final float lightPosSpot[]    = {0.0f, 0.0f, -8.0f, 1.0f};
+
+        final float pos[] = {
+                    -5.0f, -1.5f, 0.0f,
+                     0.0f, -1.5f, 0.0f,
+                     5.0f, -1.5f, 0.0f,
+                };
+        
+        final float v[] = new float[9];
+        ByteBuffer vbb = ByteBuffer.allocateDirect(v.length*4);
+        vbb.order(ByteOrder.nativeOrder());
+        FloatBuffer vb = vbb.asFloatBuffer();
+
+        gl.glDisable(GL10.GL_DITHER);
+
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuse, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, lightOff, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPosSpot, 0);
+        gl.glEnable(GL10.GL_LIGHT0);
+        
+        gl.glEnable(GL10.GL_LIGHTING);
+
+
+        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+        gl.glNormal3f(0, 0, 1);
+        
+
+        // draw first 3 triangles, without using transforms
+        for (int i=0 ; i<3 ; i++) {
+            v[0] = -1; v[1] =-1; v[2] = -10;
+            v[3] =  0; v[4] = 1; v[5] = -10;
+            v[6] =  1; v[7] =-1; v[8] = -10;
+            for (int j=0 ; j<3 ; j++) {
+                v[j*3+0] -= pos[i*3+0];
+                v[j*3+1] -= pos[i*3+1];
+                v[j*3+2] -= pos[i*3+2];
+            }
+            vb.put(v).position(0);
+            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
+            gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        }
+        
+        // draw the 2nd batch this time with transforms
+        v[0] = -1; v[1] =-1; v[2] = -10;
+        v[3] =  0; v[4] = 1; v[5] = -10;
+        v[6] =  1; v[7] =-1; v[8] = -10;
+        vb.put(v).position(0);
+        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
+
+        // draw lower left triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[0], pos[1], pos[2]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();
+
+        // draw lower middle triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[3], pos[4], pos[5]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();
+
+        // draw lower right triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[6], pos[7], pos[8]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();      
+    }
+
+    public int[] getConfigSpec() {
+        int[] configSpec = { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
+        return configSpec;      
+    }
+}
+
diff --git a/preloaded-classes b/preloaded-classes
index e72c6af..3858883 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -485,7 +485,6 @@
 android.webkit.JWebCoreJavaBridge
 android.webkit.LoadListener
 android.webkit.MimeTypeMap
-android.webkit.TextDialog
 android.webkit.URLUtil
 android.webkit.WebBackForwardList
 android.webkit.WebHistoryItem
@@ -501,14 +500,14 @@
 android.webkit.WebSettings$TextSize
 android.webkit.WebSyncManager
 android.webkit.WebSyncManager$SyncHandler
+android.webkit.WebTextView
 android.webkit.WebView
 android.webkit.WebView$ExtendedZoomControls
-android.webkit.WebView$FocusNode
 android.webkit.WebView$PrivateHandler
 android.webkit.WebViewCore
+android.webkit.WebViewCore$CursorData
 android.webkit.WebViewCore$EventHub
 android.webkit.WebViewCore$EventHub$1
-android.webkit.WebViewCore$FocusData
 android.webkit.WebViewCore$WebCoreThread
 android.webkit.WebViewCore$WebCoreThread$1
 android.webkit.WebViewDatabase
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 82ed1e3..5770a77 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -26,6 +26,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -34,11 +35,17 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.SparseArray;
 
 import android.backup.IBackupManager;
+import android.backup.BackupManager;
+
+import com.android.internal.backup.AdbTransport;
+import com.android.internal.backup.GoogleTransport;
+import com.android.internal.backup.IBackupTransport;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -59,6 +66,7 @@
     //private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
 
     private static final int MSG_RUN_BACKUP = 1;
+    private static final int MSG_RUN_FULL_BACKUP = 2;
     
     private Context mContext;
     private PackageManager mPackageManager;
@@ -89,6 +97,16 @@
     private ArrayList<BackupRequest> mBackupQueue;
     private final Object mQueueLock = new Object();
 
+    // The thread performing the sequence of queued backups binds to each app's agent
+    // in succession.  Bind notifications are asynchronously delivered through the
+    // Activity Manager; use this lock object to signal when a requested binding has
+    // completed.
+    private final Object mAgentConnectLock = new Object();
+    private IBackupAgent mConnectedAgent;
+    private volatile boolean mConnecting;
+
+    private int mTransportId;
+
     private File mStateDir;
     private File mDataDir;
     
@@ -101,6 +119,7 @@
         mStateDir = new File(Environment.getDataDirectory(), "backup");
         mStateDir.mkdirs();
         mDataDir = Environment.getDownloadCacheDirectory();
+        mTransportId = BackupManager.TRANSPORT_GOOGLE;
         
         // Build our mapping of uid to backup client services
         synchronized (mBackupParticipants) {
@@ -130,6 +149,8 @@
                 return;
             }
 
+            // !!! TODO: this is buggy right now; we wind up with duplicate participant entries
+            // after using 'adb install -r' of a participating app
             String action = intent.getAction();
             if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
                 synchronized (mBackupParticipants) {
@@ -166,7 +187,7 @@
                 // snapshot the pending-backup set and work on that
                 synchronized (mQueueLock) {
                     if (mBackupQueue == null) {
-                        mBackupQueue = new ArrayList();
+                        mBackupQueue = new ArrayList<BackupRequest>();
                         for (BackupRequest b: mPendingBackups.values()) {
                             mBackupQueue.add(b);
                         }
@@ -176,68 +197,24 @@
                     // WARNING: If we crash after this line, anything in mPendingBackups will
                     // be lost.  FIX THIS.
                 }
-                startOneAgent();
+                (new PerformBackupThread(mTransportId, mBackupQueue)).run();
+                break;
+
+            case MSG_RUN_FULL_BACKUP:
                 break;
             }
         }
     }
 
-    void startOneAgent() {
-        // Loop until we find someone to start or the queue empties out.
-        while (true) {
-            BackupRequest request;
-            synchronized (mQueueLock) {
-                int queueSize = mBackupQueue.size();
-                Log.d(TAG, "mBackupQueue.size=" + queueSize);
-                if (queueSize == 0) {
-                    mBackupQueue = null;
-                    // if there are pending backups, start those after a short delay
-                    if (mPendingBackups.size() > 0) {
-                        mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL);
-                    }
-                    return;
-                }
-                request = mBackupQueue.get(0);
-                // Take it off the queue when we're done.
-            }
-            
-            Log.d(TAG, "starting agent for " + request);
-            // !!! TODO: need to handle the restore case?
-            int mode = (request.fullBackup)
-                    ? IApplicationThread.BACKUP_MODE_FULL
-                    : IApplicationThread.BACKUP_MODE_INCREMENTAL;
-            try {
-                if (mActivityManager.bindBackupAgent(request.appInfo, mode)) {
-                    Log.d(TAG, "awaiting agent for " + request);
-                    // success
-                    return;
-                }
-            } catch (RemoteException e) {
-                // can't happen; activity manager is local
-            } catch (SecurityException ex) {
-                // Try for the next one.
-                Log.d(TAG, "error in bind", ex);
-            }
-        }
-    }
-
-    void processOneBackup(String packageName, IBackupAgent bs) {
+    void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
+        final String packageName = request.appInfo.packageName;
         Log.d(TAG, "processOneBackup doBackup() on " + packageName);
 
-        BackupRequest request;
-        synchronized (mQueueLock) {
-            if (mBackupQueue == null) {
-                Log.d(TAG, "mBackupQueue is null.  WHY?");
-            }
-            request = mBackupQueue.get(0);
-        }
-
         try {
-            // !!! TODO right now these naming schemes limit applications to
-            // one backup service per package
-            File savedStateName = new File(mStateDir, request.appInfo.packageName);
-            File backupDataName = new File(mDataDir, request.appInfo.packageName + ".data");
-            File newStateName = new File(mStateDir, request.appInfo.packageName + ".new");
+            // !!! TODO: get the state file dir from the transport
+            File savedStateName = new File(mStateDir, packageName);
+            File backupDataName = new File(mDataDir, packageName + ".data");
+            File newStateName = new File(mStateDir, packageName + ".new");
             
             // In a full backup, we pass a null ParcelFileDescriptor as
             // the saved-state "file"
@@ -259,9 +236,10 @@
                             ParcelFileDescriptor.MODE_CREATE);
 
             // Run the target's backup pass
+            boolean success = false;
             try {
-                // TODO: Make this oneway
-                bs.doBackup(savedState, backupData, newState);
+                agent.doBackup(savedState, backupData, newState);
+                success = true;
             } finally {
                 if (savedState != null) {
                     savedState.close();
@@ -270,43 +248,35 @@
                 newState.close();
             }
 
-            // !!! TODO: Now propagate the newly-backed-up data to the transport
-            
-            // !!! TODO: After successful transport, delete the now-stale data
-            // and juggle the files so that next time the new state is passed
-            //backupDataName.delete();
-            newStateName.renameTo(savedStateName);
-            
+            // Now propagate the newly-backed-up data to the transport
+            if (success) {
+                if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
+                backupData =
+                    ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
+                int error = transport.performBackup(packageName, backupData);
+
+                // !!! TODO: After successful transport, delete the now-stale data
+                // and juggle the files so that next time the new state is passed
+                //backupDataName.delete();
+                newStateName.renameTo(savedStateName);
+            }
         } catch (FileNotFoundException fnf) {
             Log.d(TAG, "File not found on backup: ");
             fnf.printStackTrace();
         } catch (RemoteException e) {
-            Log.d(TAG, "Remote target " + packageName + " threw during backup:");
+            Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:");
             e.printStackTrace();
         } catch (Exception e) {
             Log.w(TAG, "Final exception guard in backup: ");
             e.printStackTrace();
         }
-        synchronized (mQueueLock) {
-            mBackupQueue.remove(0);
-        }
-
-        if (request != null) {
-            try {
-                mActivityManager.unbindBackupAgent(request.appInfo);
-            } catch (RemoteException e) {
-                // can't happen
-            }
-        }
-
-        // start the next one
-        startOneAgent();
     }
 
     // Add the backup agents in the given package to our set of known backup participants.
     // If 'packageName' is null, adds all backup agents in the whole system.
     void addPackageParticipantsLocked(String packageName) {
         // Look for apps that define the android:backupAgent attribute
+        if (DEBUG) Log.v(TAG, "addPackageParticipantsLocked: " + packageName);
         List<ApplicationInfo> targetApps = allAgentApps();
         addPackageParticipantsLockedInner(packageName, targetApps);
     }
@@ -336,6 +306,7 @@
     // Remove the given package's backup services from our known active set.  If
     // 'packageName' is null, *all* backup services will be removed.
     void removePackageParticipantsLocked(String packageName) {
+        if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName);
         List<ApplicationInfo> allApps = null;
         if (packageName != null) {
             allApps = new ArrayList<ApplicationInfo>();
@@ -354,6 +325,13 @@
 
     private void removePackageParticipantsLockedInner(String packageName,
             List<ApplicationInfo> agents) {
+        if (DEBUG) {
+            Log.v(TAG, "removePackageParticipantsLockedInner (" + packageName
+                    + ") removing " + agents.size() + " entries");
+            for (ApplicationInfo a : agents) {
+                Log.v(TAG, "    - " + a);
+            }
+        }
         for (ApplicationInfo app : agents) {
             if (packageName == null || app.packageName.equals(packageName)) {
                 int uid = app.uid;
@@ -361,8 +339,7 @@
                 if (set != null) {
                     set.remove(app);
                     if (set.size() == 0) {
-                        mBackupParticipants.put(uid, null);
-                    }
+                        mBackupParticipants.delete(uid);                    }
                 }
             }
         }
@@ -390,6 +367,7 @@
             Log.e(TAG, "updatePackageParticipants called with null package name");
             return;
         }
+        if (DEBUG) Log.v(TAG, "updatePackageParticipantsLocked: " + packageName);
 
         // brute force but small code size
         List<ApplicationInfo> allApps = allAgentApps();
@@ -397,6 +375,128 @@
         addPackageParticipantsLockedInner(packageName, allApps);
     }
 
+    // ----- Back up a set of applications via a worker thread -----
+
+    class PerformBackupThread extends Thread {
+        private static final String TAG = "PerformBackupThread";
+        int mTransport;
+        ArrayList<BackupRequest> mQueue;
+
+        public PerformBackupThread(int transportId, ArrayList<BackupRequest> queue) {
+            mTransport = transportId;
+            mQueue = queue;
+        }
+
+        @Override
+        public void run() {
+            /*
+             * 1. start up the current transport
+             * 2. for each item in the queue:
+             *      2a. bind the agent [wait for async attach]
+             *      2b. set up the files and call doBackup()
+             *      2c. unbind the agent
+             * 3. tear down the transport
+             * 4. done!
+             */
+            if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
+
+            // stand up the current transport
+            IBackupTransport transport = null;
+            switch (mTransport) {
+            case BackupManager.TRANSPORT_ADB:
+                if (DEBUG) Log.v(TAG, "Initializing adb transport");
+                transport = new AdbTransport();
+                break;
+
+            case BackupManager.TRANSPORT_GOOGLE:
+                if (DEBUG) Log.v(TAG, "Initializing Google transport");
+                //!!! TODO: stand up the google backup transport here
+                transport = new GoogleTransport();
+                break;
+
+            default:
+                Log.e(TAG, "Perform backup with unknown transport " + mTransport);
+                // !!! TODO: re-enqueue the backup queue for later?
+                return;
+            }
+
+            try {
+                transport.startSession();
+            } catch (Exception e) {
+                Log.e(TAG, "Error starting backup session");
+                e.printStackTrace();
+                // !!! TODO: re-enqueue the backup queue for later?
+                return;
+            }
+
+            // The transport is up and running; now run all the backups in our queue
+            doQueuedBackups(transport);
+
+            // Finally, tear down the transport
+            try {
+                transport.endSession();
+            } catch (Exception e) {
+                Log.e(TAG, "Error ending transport");
+                e.printStackTrace();
+            }
+        }
+
+        private void doQueuedBackups(IBackupTransport transport) {
+            for (BackupRequest request : mQueue) {
+                Log.d(TAG, "starting agent for " + request);
+                // !!! TODO: need to handle the restore case?
+
+                IBackupAgent agent = null;
+                int mode = (request.fullBackup)
+                        ? IApplicationThread.BACKUP_MODE_FULL
+                        : IApplicationThread.BACKUP_MODE_INCREMENTAL;
+                try {
+                    synchronized(mAgentConnectLock) {
+                        mConnecting = true;
+                        mConnectedAgent = null;
+                        if (mActivityManager.bindBackupAgent(request.appInfo, mode)) {
+                            Log.d(TAG, "awaiting agent for " + request);
+
+                            // success; wait for the agent to arrive
+                            while (mConnecting && mConnectedAgent == null) {
+                                try {
+                                    mAgentConnectLock.wait(10000);
+                                } catch (InterruptedException e) {
+                                    // just retry
+                                    continue;
+                                }
+                            }
+
+                            // if we timed out with no connect, abort and move on
+                            if (mConnecting == true) {
+                                Log.w(TAG, "Timeout waiting for agent " + request);
+                                continue;
+                            }
+                            agent = mConnectedAgent;
+                        }
+                    }
+                } catch (RemoteException e) {
+                    // can't happen; activity manager is local
+                } catch (SecurityException ex) {
+                    // Try for the next one.
+                    Log.d(TAG, "error in bind", ex);
+                }
+
+                // successful bind? run the backup for this agent
+                if (agent != null) {
+                    processOneBackup(request, agent, transport);
+                }
+
+                // send the unbind even on timeout, just in case
+                try {
+                    mActivityManager.unbindBackupAgent(request.appInfo);
+                } catch (RemoteException e) {
+                    // can't happen
+                }
+            }
+        }
+    }
+
     // ----- IBackupManager binder interface -----
     
     public void dataChanged(String packageName) throws RemoteException {
@@ -432,45 +532,75 @@
                 // Schedule a backup pass in a few minutes.  As backup-eligible data
                 // keeps changing, continue to defer the backup pass until things
                 // settle down, to avoid extra overhead.
+                mBackupHandler.removeMessages(MSG_RUN_BACKUP);
                 mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL);
             }
         }
     }
 
-    // Schedule a backup pass for a given package, even if the caller is not part of
-    // that uid or package itself.
+    // Schedule a backup pass for a given package.  This method will schedule a
+    // full backup even for apps that do not declare an android:backupAgent, so
+    // use with care.
     public void scheduleFullBackup(String packageName) throws RemoteException {
-        // !!! TODO: protect with a signature-or-system permission?
+        mContext.enforceCallingPermission("android.permission.BACKUP", "scheduleFullBackup");
+
+        if (DEBUG) Log.v(TAG, "Scheduling immediate full backup for " + packageName);
         synchronized (mQueueLock) {
-            int numKeys = mBackupParticipants.size();
-            for (int index = 0; index < numKeys; index++) {
-                int uid = mBackupParticipants.keyAt(index);
-                HashSet<ApplicationInfo> servicesAtUid = mBackupParticipants.get(uid);
-                for (ApplicationInfo app: servicesAtUid) {
-                    if (app.packageName.equals(packageName)) {
-                        mPendingBackups.put(app, new BackupRequest(app, true));
-                    }
-                }
+            try {
+                ApplicationInfo app = mPackageManager.getApplicationInfo(packageName, 0);
+                mPendingBackups.put(app, new BackupRequest(app, true));
+                mBackupHandler.sendEmptyMessage(MSG_RUN_FULL_BACKUP);
+            } catch (NameNotFoundException e) {
+                Log.w(TAG, "Could not find app for " + packageName + " to schedule full backup");
             }
         }
     }
 
-    // Callback: a requested backup agent has been instantiated
+    // Select which transport to use for the next backup operation
+    public int selectBackupTransport(int transportId) {
+        mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
+
+        int prevTransport = mTransportId;
+        mTransportId = transportId;
+        return prevTransport;
+    }
+
+    // Callback: a requested backup agent has been instantiated.  This should only
+    // be called from the Activity Manager.
     public void agentConnected(String packageName, IBinder agentBinder) {
-        Log.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
-        IBackupAgent bs = IBackupAgent.Stub.asInterface(agentBinder);
-        processOneBackup(packageName, bs);
+        synchronized(mAgentConnectLock) {
+            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+                Log.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
+                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
+                mConnectedAgent = agent;
+                mConnecting = false;
+            } else {
+                Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+                        + " claiming agent connected");
+            }
+            mAgentConnectLock.notifyAll();
+        }
     }
 
     // Callback: a backup agent has failed to come up, or has unexpectedly quit.
     // If the agent failed to come up in the first place, the agentBinder argument
-    // will be null.
+    // will be null.  This should only be called from the Activity Manager.
     public void agentDisconnected(String packageName) {
         // TODO: handle backup being interrupted
+        synchronized(mAgentConnectLock) {
+            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+                mConnectedAgent = null;
+                mConnecting = false;
+            } else {
+                Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+                        + " claiming agent disconnected");
+            }
+            mAgentConnectLock.notifyAll();
+        }
     }
-    
 
-    
+
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         synchronized (mQueueLock) {
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 62b43f7..dcc3ed5 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -19,6 +19,7 @@
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
+import com.android.server.PackageManagerService.PreferredActivity;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -4149,6 +4150,7 @@
             return false;
         }
         synchronized (mPackages) {
+            grantPermissionsLP(newPkg, true);
             mSettings.writeLP();
         }
         return true;
@@ -4510,6 +4512,42 @@
         }
     }
 
+    public void replacePreferredActivity(IntentFilter filter, int match,
+            ComponentName[] set, ComponentName activity) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+        if (filter.countActions() != 1) {
+            throw new IllegalArgumentException(
+                    "replacePreferredActivity expects filter to have only 1 action.");
+        }
+        if (filter.countCategories() != 1) {
+            throw new IllegalArgumentException(
+                    "replacePreferredActivity expects filter to have only 1 category.");
+        }
+        if (filter.countDataAuthorities() != 0
+                || filter.countDataPaths() != 0
+                || filter.countDataSchemes() != 0
+                || filter.countDataTypes() != 0) {
+            throw new IllegalArgumentException(
+                    "replacePreferredActivity expects filter to have no data authorities, " +
+                    "paths, schemes or types.");
+        }
+        synchronized (mPackages) {
+            Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator();
+            String action = filter.getAction(0);
+            String category = filter.getCategory(0);
+            while (it.hasNext()) {
+                PreferredActivity pa = it.next();
+                if (pa.getAction(0).equals(action) && pa.getCategory(0).equals(category)) {
+                    it.remove();
+                    Log.i(TAG, "Removed preferred activity " + pa.mActivity + ":");
+                    filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
+                }
+            }
+            addPreferredActivity(filter, match, set, activity);
+        }
+    }
+
     public void clearPackagePreferredActivities(String packageName) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java
index 55adabb..58f8980 100644
--- a/services/java/com/android/server/ProcessStats.java
+++ b/services/java/com/android/server/ProcessStats.java
@@ -54,7 +54,10 @@
         PROC_SPACE_TERM|PROC_OUT_LONG                   // 14: stime
     };
 
+    /** Stores user time and system time in 100ths of a second. */
     private final long[] mProcessStatsData = new long[2];
+    /** Stores user time and system time in 100ths of a second. */
+    private final long[] mSinglePidStatsData = new long[2];
 
     private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] {
         PROC_SPACE_TERM,
@@ -418,7 +421,18 @@
         
         return pids;
     }
-    
+
+    public long getCpuTimeForPid(int pid) {
+        final String statFile = "/proc/" + pid + "/stat";
+        final long[] statsData = mSinglePidStatsData;
+        if (Process.readProcFile(statFile, PROCESS_STATS_FORMAT,
+                null, statsData, null)) {
+            long time = statsData[0] + statsData[1];
+            return time;
+        }
+        return 0;
+    }
+
     final public int getLastUserTime() {
         return mRelUserTime;
     }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 3b26cb7..736c0da 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -62,6 +62,7 @@
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.net.Uri;
+import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -182,7 +183,7 @@
 
     // The flags that are set for all calls we make to the package manager.
     static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES
-            | PackageManager.GET_SUPPORTS_DENSITIES;
+            | PackageManager.GET_SUPPORTS_DENSITIES | PackageManager.GET_EXPANDABLE;
     
     private static final String SYSTEM_SECURE = "ro.secure";
 
@@ -1438,7 +1439,7 @@
         synchronized (mProcessStatsThread) {
             final long now = SystemClock.uptimeMillis();
             boolean haveNewCpuStats = false;
-            
+
             if (MONITOR_CPU_USAGE &&
                     mLastCpuTime < (now-MONITOR_CPU_MIN_TIME)) {
                 mLastCpuTime = now;
@@ -2063,6 +2064,25 @@
         if (prev != null) {
             prev.resumeKeyDispatchingLocked();
         }
+
+        if (prev.app != null && prev.cpuTimeAtResume > 0 && mBatteryStatsService.isOnBattery()) {
+            long diff = 0;
+            synchronized (mProcessStatsThread) {
+                diff = mProcessStats.getCpuTimeForPid(prev.app.pid) - prev.cpuTimeAtResume;
+            }
+            if (diff > 0) {
+                BatteryStatsImpl bsi = mBatteryStatsService.getActiveStatistics();
+                synchronized (bsi) {
+                    BatteryStatsImpl.Uid.Proc ps =
+                            bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
+                            prev.info.packageName);
+                    if (ps != null) {
+                        ps.addForegroundTimeLocked(diff);
+                    }
+                }
+            }
+        }
+        prev.cpuTimeAtResume = 0; // reset it
     }
 
     /**
@@ -2095,6 +2115,17 @@
         next.resumeKeyDispatchingLocked();
         ensureActivitiesVisibleLocked(null, 0);
         mWindowManager.executeAppTransition();
+
+        // Mark the point when the activity is resuming
+        // TODO: To be more accurate, the mark should be before the onCreate,
+        //       not after the onResume. But for subsequent starts, onResume is fine.
+        if (next.app != null) {
+            synchronized (mProcessStatsThread) {
+                next.cpuTimeAtResume = mProcessStats.getCpuTimeForPid(next.app.pid);
+            }
+        } else {
+            next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
+        }
     }
 
     /**
@@ -10346,6 +10377,7 @@
                 return;
             }
 
+            long oldIdent = Binder.clearCallingIdentity();
             try {
                 IBackupManager bm = IBackupManager.Stub.asInterface(
                         ServiceManager.getService(Context.BACKUP_SERVICE));
@@ -10355,6 +10387,8 @@
             } catch (Exception e) {
                 Log.w(TAG, "Exception trying to deliver BackupAgent binding: ");
                 e.printStackTrace();
+            } finally {
+                Binder.restoreCallingIdentity(oldIdent);
             }
         }
     }
diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java
index 1789687..944ea02 100644
--- a/services/java/com/android/server/am/HistoryRecord.java
+++ b/services/java/com/android/server/am/HistoryRecord.java
@@ -66,6 +66,7 @@
     int theme;              // resource identifier of activity's theme.
     TaskRecord task;        // the task this is in.
     long startTime;         // when we starting launching this activity
+    long cpuTimeAtResume;   // the cpu time of host process at the time of resuming activity
     Configuration configuration; // configuration activity was last running in
     HistoryRecord resultTo; // who started this entry, so will get our reply
     final String resultWho; // additional identifier for use by resultTo.
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index 76c05d9..b59fe54 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -819,30 +819,10 @@
         updateDataIcon();
     }
 
-    // TODO(Teleca): I've add isCdma() to reduce some code duplication and simplify.
-    //               Please validate the correctness of these changes
     private boolean isCdma() {
-        // Is this equivalent, if so it seems simpler?
-//        return ((mPhone != null) && (mPhone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA));
-
-        if (mServiceState != null) {
-            switch(mServiceState.getRadioTechnology()) {
-                case ServiceState.RADIO_TECHNOLOGY_1xRTT:
-                case ServiceState.RADIO_TECHNOLOGY_EVDO_0:
-                case ServiceState.RADIO_TECHNOLOGY_EVDO_A:
-                case ServiceState.RADIO_TECHNOLOGY_IS95A:
-                case ServiceState.RADIO_TECHNOLOGY_IS95B:
-                  return true;
-                default:
-                  return false;
-            }
-        } else {
-            return false;
-        }
+        return ((mPhone != null) && (mPhone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA));
     }
 
-    // TODO(Teleca): I've add hasService() to reduce some code duplication and simplify.
-    //               Please validate the correctness of these changes.
     private boolean hasService() {
         if (mServiceState != null) {
             switch (mServiceState.getState()) {
@@ -1202,10 +1182,12 @@
     private final void updateCdmaRoamingIcon() {
         if (!hasService()) {
             mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
+            return;
         }
 
         if (!isCdma()) {
             mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
+            return;
         }
 
         int[] iconList = sRoamingIndicatorImages_cdma;
@@ -1235,8 +1217,10 @@
                 mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true);
                 break;
             case EriInfo.ROAMING_ICON_MODE_FLASH:
-                mCdmaRoamingIndicatorIconData.iconId = com.android.internal.R.drawable.stat_sys_roaming_cdma_flash;
+                mCdmaRoamingIndicatorIconData.iconId =
+                        com.android.internal.R.drawable.stat_sys_roaming_cdma_flash;
                 mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null);
+                mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true);
                 break;
 
         }
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index e73de3c..f9b95b2 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -517,9 +517,14 @@
         return mWrappedSmsMessage.getUserData();
     }
 
-    /* Not part of the SDK interface and only needed by specific classes:
-       protected SmsHeader getUserDataHeader()
-    */
+    /**
+     * Return the user data header (UDH).
+     *
+     * @hide
+     */
+    public SmsHeader getUserDataHeader() {
+        return mWrappedSmsMessage.getUserDataHeader();
+    }
 
     /**
      * Returns the raw PDU for the message.
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index 34a57a0..c6d1a4b 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -1258,10 +1258,7 @@
      * @param result
      *            Callback message is empty on completion
      */
-    /**
-     * TODO(Teleca): configValuesArray is represented as a RIL_BroadcastSMSConfig
-     * so we think this should be a class with the appropriate parameters not an array?
-     */
+    // TODO: Change the configValuesArray to a RIL_BroadcastSMSConfig
     public void setCdmaBroadcastConfig(int[] configValuesArray, Message result);
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 77755ce..af79404 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -93,6 +93,7 @@
     protected static final int EVENT_NV_READY = 31;
     protected static final int EVENT_PS_RESTRICT_ENABLED = 32;
     protected static final int EVENT_PS_RESTRICT_DISABLED = 33;
+    public static final int EVENT_CLEAN_UP_CONNECTION = 34;
 
     //***** Constants
     protected static final int RECONNECT_DELAY_INITIAL_MILLIS = 5 * 1000;
@@ -149,7 +150,7 @@
 
     /** Intent sent when the reconnect alarm fires. */
     protected PendingIntent mReconnectIntent = null;
-    
+
     /** CID of active data connection */
     protected int cidActive;
 
@@ -226,6 +227,7 @@
     protected abstract void onDisconnectDone(AsyncResult ar);
     protected abstract void onVoiceCallStarted();
     protected abstract void onVoiceCallEnded();
+    protected abstract void onCleanUpConnection(boolean tearDown, String reason);
 
   //***** Overridden from Handler
     public void handleMessage (Message msg) {
@@ -272,6 +274,11 @@
                 onVoiceCallEnded();
                 break;
 
+            case EVENT_CLEAN_UP_CONNECTION:
+                boolean tearDown = (msg.arg1 == 0) ? false : true;
+                onCleanUpConnection(tearDown, (String)msg.obj);
+                break;
+
             default:
                 Log.e("DATA", "Unidentified event = " + msg.what);
                 break;
@@ -279,12 +286,6 @@
     }
 
     /**
-     * Simply tear down data connections due to radio off 
-     * and don't setup again.
-     */
-    public abstract void cleanConnectionBeforeRadioOff();
-
-    /**
      * Report the current state of data connectivity (enabled or disabled)
      * @return {@code false} if data connectivity has been explicitly disabled,
      * {@code true} otherwise.
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 260f662..a6b0480 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -229,7 +229,7 @@
     /**
      * Get the current DataState. No change notification exists at this
      * interface -- use
-     * {@link com.android.internal.telephony.PhoneStateIntentReceiver PhoneStateIntentReceiver}
+     * {@link com.android.telephony.PhoneStateListener PhoneStateListener}
      * instead.
      */
     DataState getDataConnectionState();
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index ebba092..c92cf3d 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -55,15 +55,6 @@
  *
  */
 
-/**
- * TODO(Teleca): This has a multitude of methods that are CDMA specific
- * , (registerForVoicePrivacy, registerCdmaInformationRecord, registerCdmaCallWaiting,
- * setCdmaRoamingPreference, setCdmaSubscription, getCdmaEriIcon, getCdmaEriText, ...) can
- * these type of calls be more abstract. For example CallWaiting is common between the GSM/CDMA
- * it would seem that doesn't need to be cdma specific. Also, should the application be directly
- * dealing with the CdmaInformationRecord's could they be abstracted to something more generic.
- */
-
 public abstract class PhoneBase implements Phone {
     private static final String LOG_TAG = "PHONE";
     private static final boolean LOCAL_DEBUG = true;
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index ea84b098..0cdeeff 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -165,7 +165,7 @@
     }
 
     void
-    onError(int error) {
+    onError(int error, Object ret) {
         CommandException ex;
 
         ex = CommandException.fromRilErrno(error);
@@ -175,7 +175,7 @@
             + " error: " + ex);
 
         if (mResult != null) {
-            AsyncResult.forMessage(mResult, null, ex);
+            AsyncResult.forMessage(mResult, ret, ex);
             mResult.sendToTarget();
         }
 
@@ -290,7 +290,7 @@
                         s = mSocket;
 
                         if (s == null) {
-                            rr.onError(RADIO_NOT_AVAILABLE);
+                            rr.onError(RADIO_NOT_AVAILABLE, null);
                             rr.release();
                             mRequestMessagesPending--;
                             alreadySubtracted = true;
@@ -331,7 +331,7 @@
                         // make sure this request has not already been handled,
                         // eg, if RILReceiver cleared the list.
                         if (req != null || !alreadySubtracted) {
-                            rr.onError(RADIO_NOT_AVAILABLE);
+                            rr.onError(RADIO_NOT_AVAILABLE, null);
                             rr.release();
                         }
                     } catch (RuntimeException exc) {
@@ -340,7 +340,7 @@
                         // make sure this request has not already been handled,
                         // eg, if RILReceiver cleared the list.
                         if (req != null || !alreadySubtracted) {
-                            rr.onError(GENERIC_FAILURE);
+                            rr.onError(GENERIC_FAILURE, null);
                             rr.release();
                         }
                     }
@@ -545,7 +545,7 @@
                 synchronized (mRequestsList) {
                     for (int i = 0, sz = mRequestsList.size() ; i < sz ; i++) {
                         RILRequest rr = mRequestsList.get(i);
-                        rr.onError(RADIO_NOT_AVAILABLE);
+                        rr.onError(RADIO_NOT_AVAILABLE, null);
                         rr.release();
                     }
 
@@ -1986,20 +1986,16 @@
             return;
         }
 
-        if (error != 0) {
-            rr.onError(error);
-            rr.release();
-            return;
-        }
+        Object ret = null;
 
-        Object ret;
-
-        try {switch (rr.mRequest) {
-/*
+        if (error == 0 || p.dataAvail() > 0) {
+            // either command succeeds or command fails but with data payload
+            try {switch (rr.mRequest) {
+            /*
  cat libs/telephony/ril_commands.h \
  | egrep "^ *{RIL_" \
  | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: ret = \2(p); break;/'
-*/
+             */
             case RIL_REQUEST_GET_SIM_STATUS: ret =  responseIccCardStatus(p); break;
             case RIL_REQUEST_ENTER_SIM_PIN: ret =  responseVoid(p); break;
             case RIL_REQUEST_ENTER_SIM_PUK: ret =  responseVoid(p); break;
@@ -2104,17 +2100,24 @@
             default:
                 throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest);
             //break;
-        }} catch (Throwable tr) {
-            // Exceptions here usually mean invalid RIL responses
+            }} catch (Throwable tr) {
+                // Exceptions here usually mean invalid RIL responses
 
-            Log.w(LOG_TAG, rr.serialString() + "< "
-                    + requestToString(rr.mRequest)
-                    + " exception, possible invalid RIL response", tr);
+                Log.w(LOG_TAG, rr.serialString() + "< "
+                        + requestToString(rr.mRequest)
+                        + " exception, possible invalid RIL response", tr);
 
-            if (rr.mResult != null) {
-                AsyncResult.forMessage(rr.mResult, null, tr);
-                rr.mResult.sendToTarget();
+                if (rr.mResult != null) {
+                    AsyncResult.forMessage(rr.mResult, null, tr);
+                    rr.mResult.sendToTarget();
+                }
+                rr.release();
+                return;
             }
+        }
+
+        if (error != 0) {
+            rr.onError(error, ret);
             rr.release();
             return;
         }
@@ -3302,10 +3305,7 @@
         send(rr);
     }
 
-    /**
-     * TODO(Teleca): configValuesArray is represented as a RIL_BroadcastSMSConfig
-     * so we think this should be a class with the appropriate parameters not an array?
-     */
+    // TODO: Change the configValuesArray to a RIL_BroadcastSMSConfig
     public void setCdmaBroadcastConfig(int[] configValuesArray, Message response) {
         RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG, response);
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index c0bfe5e..0ebe507 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -38,6 +38,7 @@
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_INECM_MODE;
 
 import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.DataConnection;
@@ -249,7 +250,7 @@
     public DataActivityState getDataActivityState() {
         DataActivityState ret = DataActivityState.NONE;
 
-        if (mSST.getCurrentCdmaDataConnectionState() != ServiceState.RADIO_TECHNOLOGY_UNKNOWN) {
+        if (mSST.getCurrentCdmaDataConnectionState() == ServiceState.STATE_IN_SERVICE) {
 
             switch (mDataConnection.getActivity()) {
                 case DATAIN:
@@ -384,11 +385,11 @@
     }
 
     public String getLine1Number() {
-        return mRuimRecords.getMdnNumber();
+        return mSST.getMdnNumber();
     }
 
     public String getCdmaMIN() {
-        return mRuimRecords.getCdmaMin();
+        return mSST.getCdmaMin();
     }
 
     public void getCallWaiting(Message onComplete) {
@@ -525,8 +526,20 @@
 
     public void
     getNeighboringCids(Message response) {
-        // WINK:TODO: implement after Cupcake merge
-        mCM.getNeighboringCids(response); // workaround.
+        /*
+         * This is currently not implemented.  At least as of June
+         * 2009, there is no neighbor cell information available for
+         * CDMA because some party is resisting making this
+         * information readily available.  Consequently, calling this
+         * function can have no useful effect.  This situation may
+         * (and hopefully will) change in the future.
+         */
+        if (response != null) {
+            CommandException ce = new CommandException(
+                    CommandException.Error.REQUEST_NOT_SUPPORTED);
+            AsyncResult.forMessage(response).exception = ce;
+            response.sendToTarget();
+        }
     }
 
     public DataState getDataConnectionState() {
@@ -545,8 +558,7 @@
              // already been called
 
              ret = DataState.DISCONNECTED;
-        } else if (mSST.getCurrentCdmaDataConnectionState()
-                == ServiceState.RADIO_TECHNOLOGY_UNKNOWN) {
+        } else if (mSST.getCurrentCdmaDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
             // If we're out of service, open TCP sockets may still work
             // but no data will flow
             ret = DataState.DISCONNECTED;
@@ -1159,61 +1171,7 @@
     public int getCdmaEriIconIndex() {
         int roamInd = getServiceState().getCdmaRoamingIndicator();
         int defRoamInd = getServiceState().getCdmaDefaultRoamingIndicator();
-        int ret = -1;
-
-        switch (roamInd) {
-            // Handling the standard roaming indicator (non-ERI)
-            case EriInfo.ROAMING_INDICATOR_ON:
-            case EriInfo.ROAMING_INDICATOR_OFF:
-            case EriInfo.ROAMING_INDICATOR_FLASH:
-                Log.d(LOG_TAG, "Using Standard Roaming Indicator (non-ERI): " + roamInd);
-                ret = roamInd;
-            break;
-
-            // Handling the Enhanced Roaming Indicator (roamInd > 2)
-            default:
-                if (!mEriManager.isEriFileLoaded()) {
-                    /**
-                     * TODO(Teleca): What is going on here? Conditionals on the variable being
-                     * switched? Seems unreasonably confusing... Especially since the above comment
-                     * indicates this should always be true... If we used explicit returns, the
-                     * switch could be used to filter specific cases for early bail, and the rest
-                     * could then be dealt with outside the switch...
-                     */
-
-                    if(defRoamInd > 2) {
-                        Log.d(LOG_TAG, "ERI File not loaded, using: "
-                                + EriInfo.ROAMING_INDICATOR_FLASH);
-                        ret = EriInfo.ROAMING_INDICATOR_FLASH;
-                    } else {
-                        Log.d(LOG_TAG, "ERI File not loaded, using: " + defRoamInd);
-                        ret = defRoamInd;
-                    }
-                } else if (mEriManager.getEriInfo(roamInd) == null) {
-                    if(mEriManager.getEriInfo(defRoamInd) == null) {
-/**
- * TODO(Teleca): Why the redundant code? Especially since it results in this very strange looking
- * almost-identical conditional... How about calling each version of mEriManager.getEriInfo just
- * once, and conditionalizing on the results..
- */
-                        Log.e(LOG_TAG, "Error: ERI entry: " + roamInd
-                                + " not present, defRoamInd: " + defRoamInd
-                                + " not defined in ERI file");
-                        ret = EriInfo.ROAMING_INDICATOR_ON;
-                    } else {
-                        int iconIndex = mEriManager.getEriInfo(defRoamInd).mIconIndex;
-                        Log.d(LOG_TAG, "ERI entry " + roamInd + " not present, using icon: "
-                                + iconIndex);
-                        ret = iconIndex;
-                    }
-                } else {
-                    int iconIndex = mEriManager.getEriInfo(roamInd).mIconIndex;
-                    Log.d(LOG_TAG, "Using ERI icon: " + iconIndex);
-                    ret = iconIndex;
-                }
-            break;
-        }
-        return ret;
+        return mEriManager.getCdmaEriIconIndex(roamInd, defRoamInd);
     }
 
     /**
@@ -1225,60 +1183,7 @@
     public int getCdmaEriIconMode() {
         int roamInd = getServiceState().getCdmaRoamingIndicator();
         int defRoamInd = getServiceState().getCdmaDefaultRoamingIndicator();
-        int ret = -1;
-
-        switch (roamInd) {
-            // Handling the standard roaming indicator (non-ERI)
-            case EriInfo.ROAMING_INDICATOR_ON:
-            case EriInfo.ROAMING_INDICATOR_OFF:
-                Log.d(LOG_TAG, "Using Standard Roaming Indicator (non-ERI): normal");
-                ret = EriInfo.ROAMING_ICON_MODE_NORMAL;
-            break;
-
-            case EriInfo.ROAMING_INDICATOR_FLASH:
-                Log.d(LOG_TAG, "Using Standard Roaming Indicator (non-ERI): flashing");
-                ret = EriInfo.ROAMING_ICON_MODE_FLASH;
-            break;
-
-            // Handling the Enhanced Roaming Indicator (roamInd > 2)
-            default:
-                if (!mEriManager.isEriFileLoaded()) {
-                    if(defRoamInd > 2) {
-                        Log.d(LOG_TAG, "ERI File not loaded, defRoamInd > 2, flashing");
-                        ret = EriInfo.ROAMING_ICON_MODE_FLASH;
-                    } else {
-                        switch (defRoamInd) {
-                            // Handling the standard roaming indicator (non-ERI)
-                            case EriInfo.ROAMING_INDICATOR_ON:
-                            case EriInfo.ROAMING_INDICATOR_OFF:
-                                Log.d(LOG_TAG, "ERI File not loaded, normal");
-                                ret = EriInfo.ROAMING_ICON_MODE_NORMAL;
-                            break;
-
-                            case EriInfo.ROAMING_INDICATOR_FLASH:
-                                Log.d(LOG_TAG, "ERI File not loaded, normal");
-                                ret = EriInfo.ROAMING_ICON_MODE_FLASH;
-                            break;
-                        }
-                    }
-                } else if (mEriManager.getEriInfo(roamInd) == null) {
-                    if(mEriManager.getEriInfo(defRoamInd) == null) {
-                        Log.e(LOG_TAG, "Error: defRoamInd not defined in ERI file, normal");
-                        ret =  EriInfo.ROAMING_ICON_MODE_NORMAL;
-                    } else {
-                        int mode = mEriManager.getEriInfo(defRoamInd).mIconMode;
-                        Log.d(LOG_TAG, "ERI entry " + roamInd + " not present, icon  mode: "
-                                + mode);
-                        ret = mode;
-                    }
-                } else {
-                    int mode = mEriManager.getEriInfo(roamInd).mIconMode;
-                    Log.d(LOG_TAG, "Using ERI icon mode: " + mode);
-                    ret = mode;
-                }
-            break;
-        }
-        return ret;
+        return mEriManager.getCdmaEriIconMode(roamInd, defRoamInd);
     }
 
     /**
@@ -1288,94 +1193,6 @@
     public String getCdmaEriText() {
         int roamInd = getServiceState().getCdmaRoamingIndicator();
         int defRoamInd = getServiceState().getCdmaDefaultRoamingIndicator();
-        String ret = "ERI text";
-
-        switch (roamInd) {
-            // Handling the standard roaming indicator (non-ERI)
-            case EriInfo.ROAMING_INDICATOR_ON:
-                ret = EriInfo.ROAMING_TEXT_0;
-            break;
-            case EriInfo.ROAMING_INDICATOR_OFF:
-                ret = EriInfo.ROAMING_TEXT_1;
-            break;
-            case EriInfo.ROAMING_INDICATOR_FLASH:
-                ret = EriInfo.ROAMING_TEXT_2;
-            break;
-
-            // Handling the standard ERI
-            case 3:
-                ret = EriInfo.ROAMING_TEXT_3;
-            break;
-            case 4:
-                ret = EriInfo.ROAMING_TEXT_4;
-            break;
-            case 5:
-                ret = EriInfo.ROAMING_TEXT_5;
-            break;
-            case 6:
-                ret = EriInfo.ROAMING_TEXT_6;
-            break;
-            case 7:
-                ret = EriInfo.ROAMING_TEXT_7;
-            break;
-            case 8:
-                ret = EriInfo.ROAMING_TEXT_8;
-            break;
-            case 9:
-                ret = EriInfo.ROAMING_TEXT_9;
-            break;
-            case 10:
-                ret = EriInfo.ROAMING_TEXT_10;
-            break;
-            case 11:
-                ret = EriInfo.ROAMING_TEXT_11;
-            break;
-            case 12:
-                ret = EriInfo.ROAMING_TEXT_12;
-            break;
-
-            // Handling the non standard Enhanced Roaming Indicator (roamInd > 63)
-            default:
-                if (!mEriManager.isEriFileLoaded()) {
-                    if(defRoamInd > 2) {
-                        Log.d(LOG_TAG, "ERI File not loaded, defRoamInd > 2, " +
-                                EriInfo.ROAMING_TEXT_2);
-                        ret = EriInfo.ROAMING_TEXT_2;
-                    } else {
-                        switch (defRoamInd) {
-                            // Handling the standard roaming indicator (non-ERI)
-                            case EriInfo.ROAMING_INDICATOR_ON:
-                                Log.d(LOG_TAG, "ERI File not loaded, " + EriInfo.ROAMING_TEXT_0);
-                                ret = EriInfo.ROAMING_TEXT_0;
-                            break;
-                            case EriInfo.ROAMING_INDICATOR_OFF:
-                                Log.d(LOG_TAG, "ERI File not loaded, " + EriInfo.ROAMING_TEXT_1);
-                                ret = EriInfo.ROAMING_TEXT_1;
-                            break;
-                            case EriInfo.ROAMING_INDICATOR_FLASH:
-                                Log.d(LOG_TAG, "ERI File not loaded, " + EriInfo.ROAMING_TEXT_2);
-                                ret = EriInfo.ROAMING_TEXT_2;
-                            break;
-                        }
-                    }
-                } else if (mEriManager.getEriInfo(roamInd) == null) {
-                    if(mEriManager.getEriInfo(defRoamInd) == null) {
-                        Log.e(LOG_TAG, "Error: defRoamInd not defined in ERI file, "
-                                + EriInfo.ROAMING_TEXT_0);
-                        ret = EriInfo.ROAMING_TEXT_0;
-                    } else {
-                        String eriText = mEriManager.getEriInfo(defRoamInd).mEriText;
-                        Log.d(LOG_TAG, "ERI entry " + roamInd + " not present, eri text: "
-                                + eriText);
-                        ret = eriText;
-                    }
-                } else {
-                    String eriText = mEriManager.getEriInfo(roamInd).mEriText;
-                    Log.d(LOG_TAG, "Using ERI text: " + eriText);
-                    ret = eriText;
-                }
-            break;
-        }
-        return ret;
+        return mEriManager.getCdmaEriText(roamInd, defRoamInd);
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CallFailCause.java b/telephony/java/com/android/internal/telephony/cdma/CallFailCause.java
index 9af245c..fb5f0fa 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CallFailCause.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CallFailCause.java
@@ -29,21 +29,12 @@
     // Busy Tone
     static final int USER_BUSY                      = 17;
 
-    // TODO(Teleca): Should we remove commented out values?
-//    // No Tone
-//    static final int NUMBER_CHANGED               = 22;
-//    static final int STATUS_ENQUIRY               = 30;
     static final int NORMAL_UNSPECIFIED             = 31;
-//
-//    // Congestion Tone
+
+    // Congestion Tone
     static final int NO_CIRCUIT_AVAIL               = 34;
-//    static final int TEMPORARY_FAILURE            = 41;
-//    static final int SWITCHING_CONGESTION         = 42;
-//    static final int CHANNEL_NOT_AVAIL            = 44;
-//    static final int QOS_NOT_AVAIL                = 49;
-//    static final int BEARER_NOT_AVAIL             = 58;
-//
-//    // others
+
+    // others
     static final int ACM_LIMIT_EXCEEDED             = 68;
     static final int CALL_BARRED                    = 240;
     static final int FDN_BLOCKED                    = 241;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index d8a6a50..900480f 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -263,14 +263,6 @@
     }
 
     /**
-     * Simply tear down data connections due to radio off
-     * and don't setup again.
-     */
-    public void cleanConnectionBeforeRadioOff() {
-        cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF);
-    }
-
-    /**
      * The data connection is expected to be setup while device
      *  1. has ruim card or non-volatile data store
      *  2. registered to data connection service
@@ -361,9 +353,7 @@
         boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState();
 
         if ((state == State.IDLE || state == State.SCANNING)
-                && (psState == ServiceState.RADIO_TECHNOLOGY_1xRTT ||
-                    psState == ServiceState.RADIO_TECHNOLOGY_EVDO_0 ||
-                    psState == ServiceState.RADIO_TECHNOLOGY_EVDO_A)
+                && (psState == ServiceState.STATE_IN_SERVICE)
                 && ((phone.mCM.getRadioState() == CommandsInterface.RadioState.NV_READY) ||
                         mCdmaPhone.mRuimRecords.getRecordsLoaded())
                 && (mCdmaPhone.mSST.isConcurrentVoiceAndData() ||
@@ -823,6 +813,13 @@
         }
     }
 
+    /**
+     * @override com.android.internal.telephony.DataConnectionTracker
+     */
+    protected void onCleanUpConnection(boolean tearDown, String reason) {
+        cleanUpConnection(tearDown, reason);
+    }
+
     private boolean tryAgain(FailCause cause) {
         return (cause != FailCause.RADIO_NOT_AVAILABLE)
             && (cause != FailCause.RADIO_OFF)
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 8ecdecd..12ceeaf 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -69,36 +69,26 @@
  * {@hide}
  */
 final class CdmaServiceStateTracker extends ServiceStateTracker {
+
     //***** Instance Variables
     CDMAPhone phone;
     CdmaCellLocation cellLoc;
     CdmaCellLocation newCellLoc;
 
     /**
-     * TODO(Teleca): I don't think the initialization to -1 for all of these are
-     * really necessary, I don't seem them in GsmServiceStateTracker. Also,
-     * all of the other initialization is unnecessary as I believe Java guarantees
-     * 0, false & null, but if you think it's better than do all of them there are
-     * a few that aren't initialized.
-     */
-
-    /**
      *  The access technology currently in use: DATA_ACCESS_
      */
     private int networkType = 0;
     private int newNetworkType = 0;
 
     private boolean mCdmaRoaming = false;
-    private int mRoamingIndicator = -1;
-    private int mIsInPrl = -1;
-    private int mDefaultRoamingIndicator = -1;
+    private int mRoamingIndicator;
+    private boolean mIsInPrl;
+    private int mDefaultRoamingIndicator;
 
-    /**
-     * TODO(Teleca): Maybe these should be initialized to STATE_OUT_OF_SERVICE like gprsState
-     * in GsmServiceStateTracker and remove the comment.
-     */
-    private int cdmaDataConnectionState = -1; // Initially we assume no data connection
-    private int newCdmaDataConnectionState = -1; // Initially we assume no data connection
+    // Initially we assume no data connection
+    private int cdmaDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
+    private int newCdmaDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
     private int mRegistrationState = -1;
     private RegistrantList cdmaDataConnectionAttachedRegistrants = new RegistrantList();
     private RegistrantList cdmaDataConnectionDetachedRegistrants = new RegistrantList();
@@ -117,7 +107,7 @@
 
     // We can't register for SIM_RECORDS_LOADED immediately because the
     // SIMRecords object may not be instantiated yet.
-    private boolean mNeedToRegForRuimLoaded;
+    private boolean mNeedToRegForRuimLoaded = false;
 
     // Wake lock used while setting time of day.
     private PowerManager.WakeLock mWakeLock;
@@ -125,22 +115,20 @@
 
     // Keep track of SPN display rules, so we only broadcast intent if something changes.
     private String curSpn = null;
-    private String curEriText = null;
+    private String curPlmn = null; // it contains the name of the registered network in CDMA can
+                                   // be the ONS or ERI text
     private int curSpnRule = 0;
 
-    private String mMdn = null;
-    private int mHomeSystemId = -1;
-    private int mHomeNetworkId = -1;
-    private String mMin = null;
+    private String mMdn;
+    private int mHomeSystemId;
+    private int mHomeNetworkId;
+    private String mMin;
+
     private boolean isEriTextLoaded = false;
     private boolean isSubscriptionFromRuim = false;
 
-    /**
-     * TODO(Teleca): Is this purely for debugging purposes, or do we expect this string to be
-     * passed around (eg, to the UI)? If the latter, it would be better to pass around a
-     * reasonCode, and let the UI provide its own strings.
-     */
-    private String mRegistrationDeniedReason = null;
+    // Registration Denied Reason, General/Authentication Failure, used only for debugging purposes
+    private String mRegistrationDeniedReason;
 
     //***** Constants
     static final String LOG_TAG = "CDMA";
@@ -243,9 +231,7 @@
         Registrant r = new Registrant(h, what, obj);
         cdmaDataConnectionAttachedRegistrants.add(r);
 
-        if (cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_1xRTT
-           || cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_0
-           || cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_A) {
+        if (cdmaDataConnectionState == ServiceState.STATE_IN_SERVICE) {
             r.notifyRegistrant();
         }
     }
@@ -264,9 +250,7 @@
         Registrant r = new Registrant(h, what, obj);
         cdmaDataConnectionDetachedRegistrants.add(r);
 
-        if (cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_1xRTT
-           && cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_0
-           && cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_A) {
+        if (cdmaDataConnectionState != ServiceState.STATE_IN_SERVICE) {
             r.notifyRegistrant();
         }
     }
@@ -465,7 +449,10 @@
                         (dcTracker.getAnyDataEnabled() ? 1 : 0) );
                 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_DATA_STATE_RADIO_OFF, val);
             }
-            dcTracker.cleanConnectionBeforeRadioOff();
+            Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION);
+            msg.arg1 = 1; // tearDown is true
+            msg.obj = CDMAPhone.REASON_RADIO_TURNED_OFF;
+            sendMessage(msg);
 
             // Poll data state up to 15 times, with a 100ms delay
             // totaling 1.5 sec. Normal data disable action will finish in 100ms.
@@ -484,30 +471,40 @@
     }
 
     protected void updateSpnDisplay() {
+        String spn = "";
+        boolean showSpn = false;
+        String plmn = "";
+        boolean showPlmn = false;
+        int rule = 0;
+        if (cm.getRadioState().isRUIMReady()) {
+            // TODO RUIM SPN is not implemnted, EF_SPN has to be read and Display Condition
+            //   Character Encoding, Language Indicator and SPN has to be set
+            // rule = phone.mRuimRecords.getDisplayRule(ss.getOperatorNumeric());
+            // spn = phone.mSIMRecords.getServiceProvideName();
+            plmn = ss.getOperatorAlphaLong(); // mOperatorAlphaLong contains the ONS
+            // showSpn = (rule & ...
+            showPlmn = true; // showPlmn = (rule & ...
 
-        // TODO(Teleca): Check this method again, because it is not sure at the moment how
-        // the RUIM handles the SIM stuff. Please complete this function.
+        } else {
+            // In this case there is no SPN available from RUIM, we show the ERI text
+            plmn = ss.getOperatorAlphaLong(); // mOperatorAlphaLong contains the ERI text
+            showPlmn = true;
+        }
 
-        //int rule = phone.mRuimRecords.getDisplayRule(ss.getOperatorNumeric());
-        String spn = null; //phone.mRuimRecords.getServiceProviderName();
-        String eri = ss.getOperatorAlphaLong();
-
-        if (!TextUtils.equals(this.curEriText, eri)) {
-            //TODO  (rule & SIMRecords.SPN_RULE_SHOW_SPN) == SIMRecords.SPN_RULE_SHOW_SPN;
-            boolean showSpn = false;
-            //TODO  (rule & SIMRecords.SPN_RULE_SHOW_PLMN) == SIMRecords.SPN_RULE_SHOW_PLMN;
-            boolean showEri = true;
+        if (rule != curSpnRule
+                || !TextUtils.equals(spn, curSpn)
+                || !TextUtils.equals(plmn, curPlmn)) {
             Intent intent = new Intent(Intents.SPN_STRINGS_UPDATED_ACTION);
             intent.putExtra(Intents.EXTRA_SHOW_SPN, showSpn);
             intent.putExtra(Intents.EXTRA_SPN, spn);
-            intent.putExtra(Intents.EXTRA_SHOW_PLMN, showEri);
-            intent.putExtra(Intents.EXTRA_PLMN, eri);
+            intent.putExtra(Intents.EXTRA_SHOW_PLMN, showPlmn);
+            intent.putExtra(Intents.EXTRA_PLMN, plmn);
             phone.getContext().sendStickyBroadcast(intent);
         }
 
-        //curSpnRule = rule;
-        //curSpn = spn;
-        this.curEriText = eri;
+        curSpnRule = rule;
+        curSpn = spn;
+        curPlmn = plmn;
     }
 
     /**
@@ -549,54 +546,39 @@
             }
         } else try {
             switch (what) {
-            case EVENT_POLL_STATE_REGISTRATION_CDMA: // Handle RIL_REQUEST_REGISTRATION_STATE,
-                                                     // the offset is because we don't want the
-                                                     // first 3 values in the
-                                                     // responseValuesRegistrationState array.
-                final int offset = 3;
+            case EVENT_POLL_STATE_REGISTRATION_CDMA: // Handle RIL_REQUEST_REGISTRATION_STATE.
                 states = (String[])ar.result;
 
-                /**
-                 * TODO(Teleca): Change from array to a "Class" or local
-                 * variables so names instead of index's can be used.
-                 */
-                int responseValuesRegistrationState[] = {
-                        -1, //[0] radioTechnology
-                        -1, //[1] baseStationId
-                        -1, //[2] baseStationLatitude
-                        -1, //[3] baseStationLongitude
-                         0, //[4] cssIndicator; init with 0, because it is treated as a boolean
-                        -1, //[5] systemId
-                        -1, //[6] networkId
-                        -1, //[7] Roaming indicator
-                        -1, //[8] Indicates if current system is in PRL
-                        -1, //[9] Is default roaming indicator from PRL
-                        -1, //[10] If registration state is 3 this is reason for denial
-                };
+                int registrationState = 4;     //[0] registrationState
+                int radioTechnology = -1;      //[3] radioTechnology
+                int baseStationId = -1;        //[4] baseStationId
+                int baseStationLatitude = -1;  //[5] baseStationLatitude
+                int baseStationLongitude = -1; //[6] baseStationLongitude
+                int cssIndicator = 0;          //[7] init with 0, because it is treated as a boolean
+                int systemId = 0;              //[8] systemId
+                int networkId = 0;             //[9] networkId
+                int roamingIndicator = -1;     //[10] Roaming indicator
+                int systemIsInPrl = 0;         //[11] Indicates if current system is in PRL
+                int defaultRoamingIndicator = 0;  //[12] Is default roaming indicator from PRL
+                int reasonForDenial = 0;       //[13] Denial reason if registrationState = 3
 
                 if (states.length == 14) {
                     try {
-                        this.mRegistrationState = Integer.parseInt(states[0]);
-                    } catch (NumberFormatException ex) {
-                        Log.w(LOG_TAG, "error parsing RegistrationState: " + ex);
-                    }
-                    try {
-                        responseValuesRegistrationState[0] = Integer.parseInt(states[3]);
-                        responseValuesRegistrationState[1] = Integer.parseInt(states[4], 16);
-                        responseValuesRegistrationState[2] = Integer.parseInt(states[5], 16);
-                        responseValuesRegistrationState[3] = Integer.parseInt(states[6], 16);
-                        responseValuesRegistrationState[4] = Integer.parseInt(states[7]);
-                        responseValuesRegistrationState[5] = Integer.parseInt(states[8]);
-                        responseValuesRegistrationState[6] = Integer.parseInt(states[9]);
-                        responseValuesRegistrationState[7] = Integer.parseInt(states[10]);
-                        responseValuesRegistrationState[8] = Integer.parseInt(states[11]);
-                        responseValuesRegistrationState[9] = Integer.parseInt(states[12]);
-                        responseValuesRegistrationState[10] = Integer.parseInt(states[13]);
+                        registrationState = Integer.parseInt(states[0]);
+                        radioTechnology = Integer.parseInt(states[3]);
+                        baseStationId = Integer.parseInt(states[4], 16);
+                        baseStationLatitude = Integer.parseInt(states[5], 16);
+                        baseStationLongitude = Integer.parseInt(states[6], 16);
+                        cssIndicator = Integer.parseInt(states[7]);
+                        systemId = Integer.parseInt(states[8]);
+                        networkId = Integer.parseInt(states[9]);
+                        roamingIndicator = Integer.parseInt(states[10]);
+                        systemIsInPrl = Integer.parseInt(states[11]);
+                        defaultRoamingIndicator = Integer.parseInt(states[12]);
+                        reasonForDenial = Integer.parseInt(states[13]);
                     }
                     catch(NumberFormatException ex) {
-                        Log.w(LOG_TAG, "Warning! There is an unexpected value"
-                            + "returned as response from "
-                            + "RIL_REQUEST_REGISTRATION_STATE.");
+                        Log.w(LOG_TAG, "error parsing RegistrationState: " + ex);
                     }
                 } else {
                     throw new RuntimeException("Warning! Wrong number of parameters returned from "
@@ -604,29 +586,28 @@
                                          + states.length);
                 }
 
-                mCdmaRoaming = regCodeIsRoaming(this.mRegistrationState);
-                this.newCdmaDataConnectionState =
-                    radioTechnologyToServiceState(responseValuesRegistrationState[0]);
-                newSS.setState (regCodeToServiceState(this.mRegistrationState));
-                newSS.setRadioTechnology(responseValuesRegistrationState[0]);
-                newSS.setCssIndicator(responseValuesRegistrationState[4]);
-                newSS.setSystemAndNetworkId(responseValuesRegistrationState[5],
-                    responseValuesRegistrationState[6]);
+                mRegistrationState = registrationState;
+                mCdmaRoaming = regCodeIsRoaming(registrationState);
+                newSS.setState (regCodeToServiceState(registrationState));
 
-                mRoamingIndicator = responseValuesRegistrationState[7];
-                mIsInPrl = responseValuesRegistrationState[8];
-                mDefaultRoamingIndicator = responseValuesRegistrationState[9];
+                this.newCdmaDataConnectionState = radioTechnologyToDataServiceState(radioTechnology);
+                newSS.setRadioTechnology(radioTechnology);
+                newNetworkType = radioTechnology;
 
-                newNetworkType = responseValuesRegistrationState[0];
+                newSS.setCssIndicator(cssIndicator);
+                newSS.setSystemAndNetworkId(systemId, networkId);
+                mRoamingIndicator = roamingIndicator;
+                mIsInPrl = (systemIsInPrl == 0) ? false : true;
+                mDefaultRoamingIndicator = defaultRoamingIndicator;
+
 
                 // values are -1 if not available
-                newCellLoc.setCellLocationData(responseValuesRegistrationState[1],
-                                               responseValuesRegistrationState[2],
-                                               responseValuesRegistrationState[3]);
+                newCellLoc.setCellLocationData(baseStationId, baseStationLatitude,
+                        baseStationLongitude);
 
-                if (responseValuesRegistrationState[10] == 0) {
+                if (reasonForDenial == 0) {
                     mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_GEN;
-                } else if (responseValuesRegistrationState[10] == 1) {
+                } else if (reasonForDenial == 1) {
                     mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_AUTH;
                 } else {
                     mRegistrationDeniedReason = "";
@@ -641,9 +622,7 @@
                 String opNames[] = (String[])ar.result;
 
                 if (opNames != null && opNames.length >= 3) {
-                    // TODO(Teleca): Is this necessary here and in the else clause?
-                    newSS.setOperatorName(opNames[0], opNames[1], opNames[2]);
-                    if (phone.mCM.getRadioState().isNVReady()) {
+                    if (cm.getRadioState().isNVReady()) {
                         // In CDMA in case on NV the ss.mOperatorAlphaLong is set later with the
                         // ERI text, so here is ignored what is coming from the modem
                         newSS.setOperatorName(null, opNames[1], opNames[2]);
@@ -695,28 +674,25 @@
                 newSS.setRoaming(mCdmaRoaming);
             }
 
-            /**
-             * TODO(Teleca): This would be simpler if mIsInPrl was a "boolean" as the
-             * name implies rather than tri-state. Above I've suggested that the -1's
-             * might be able to be removed, if so please simplify this. Otherwise change
-             * the name to mPrlState or some such. Also the logic can be simplified
-             * by testing for "mIsInPrl" only once.
-             */
             // Setting SS CdmaRoamingIndicator and CdmaDefaultRoamingIndicator
-            // TODO(Teleca): use constants for the standard roaming indicators
-            if (mIsInPrl == 0 && mRegistrationState == 5) {
-                // System is acquired but prl not loaded or no prl match
-                newSS.setCdmaRoamingIndicator(2); //FLASHING
-            } else if (!namMatch && (mIsInPrl == 1)) {
-                // System is acquired, no nam match, prl match
-                newSS.setCdmaRoamingIndicator(mRoamingIndicator);
-            } else if (namMatch && (mIsInPrl == 1) && mRoamingIndicator <= 2) {
-                // System is acquired, nam match, prl match, mRoamingIndicator <= 2
-                newSS.setCdmaRoamingIndicator(1); //OFF
-            } else if (namMatch && (mIsInPrl == 1) && mRoamingIndicator > 2) {
-                // System is acquired, nam match, prl match, mRoamingIndicator > 2
-                newSS.setCdmaRoamingIndicator(mRoamingIndicator);
+            // TODO(Teleca): Validate this is correct.
+            if (mIsInPrl) {
+                if (namMatch && (mRoamingIndicator <= 2)) {
+                        // System is acquired, prl match, nam match and mRoamingIndicator <= 2
+                        newSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_OFF);
+                } else {
+                    // System is acquired, prl match, no nam match  or mRoamingIndicator > 2
+                    newSS.setCdmaRoamingIndicator(mRoamingIndicator);
+                }
+            } else {
+                if (mRegistrationState == 5) {
+                    // System is acquired but prl not loaded or no prl match
+                    newSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_FLASH);
+                } else {
+                    // Use the default indicator
+                }
             }
+
             newSS.setCdmaDefaultRoamingIndicator(mDefaultRoamingIndicator);
 
             // NOTE: Some operator may require to override the mCdmaRoaming (set by the modem)
@@ -725,7 +701,7 @@
             if (DBG) {
                 log("Set CDMA Roaming Indicator to: " + newSS.getCdmaRoamingIndicator()
                     + ". mCdmaRoaming = " + mCdmaRoaming + ",  namMatch = " + namMatch
-                    + ", mIsInPrl= " + mIsInPrl + ", mRoamingIndicator = " + mRoamingIndicator
+                    + ", mIsInPrl = " + mIsInPrl + ", mRoamingIndicator = " + mRoamingIndicator
                     + ", mDefaultRoamingIndicator= " + mDefaultRoamingIndicator);
             }
             pollStateDone();
@@ -882,20 +858,12 @@
             && newSS.getState() != ServiceState.STATE_IN_SERVICE;
 
         boolean hasCdmaDataConnectionAttached =
-            (this.cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_1xRTT
-                    && this.cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_0
-                    && this.cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_A)
-                    && (this.newCdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_1xRTT
-                    || this.newCdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_0
-                    || this.newCdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_A);
+            this.cdmaDataConnectionState != ServiceState.STATE_IN_SERVICE
+            && this.newCdmaDataConnectionState == ServiceState.STATE_IN_SERVICE;
 
         boolean hasCdmaDataConnectionDetached =
-            (this.cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_1xRTT
-                    || this.cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_0
-                    || this.cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_A)
-                    && (this.newCdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_1xRTT
-                    && this.newCdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_0
-                    && this.newCdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_A);
+            this.cdmaDataConnectionState == ServiceState.STATE_IN_SERVICE
+            && this.newCdmaDataConnectionState != ServiceState.STATE_IN_SERVICE;
 
         boolean hasCdmaDataConnectionChanged =
                        cdmaDataConnectionState != newCdmaDataConnectionState;
@@ -938,18 +906,16 @@
         }
 
         if (hasChanged) {
-            if (phone.mCM.getRadioState().isNVReady()) {
+            if (cm.getRadioState().isNVReady()) {
                 String eriText;
                 // Now the CDMAPhone sees the new ServiceState so it can get the new ERI text
                 if (ss.getState() == ServiceState.STATE_IN_SERVICE) {
                     eriText = phone.getCdmaEriText();
                 } else {
-                    // Note that this is valid only for mRegistrationState 2,3,4, not 0!
-                    /**
-                     * TODO(Teleca): From the comment this apparently isn't always true
-                     * should there be additional logic with other strings?
-                     */
-                    eriText = EriInfo.SEARCHING_TEXT;
+                    // Note that ServiceState.STATE_OUT_OF_SERVICE is valid used for
+                    // mRegistrationState 0,2,3 and 4
+                    eriText = phone.getContext().getText(
+                            com.android.internal.R.string.roamingTextSearching).toString();
                 }
                 ss.setCdmaEriText(eriText);
             }
@@ -1047,6 +1013,12 @@
         return guess;
     }
 
+    /**
+     * TODO: This code is exactly the same as in GsmServiceStateTracker
+     * and has a TODO to not poll signal strength if screen is off.
+     * This code should probably be hoisted to the base class so
+     * the fix, when added, works for both.
+     */
     private void
     queueNextSignalStrengthPoll() {
         if (dontPollSignalStrength || (cm.getRadioState().isGsm())) {
@@ -1060,7 +1032,7 @@
         msg = obtainMessage();
         msg.what = EVENT_POLL_SIGNAL_STRENGTH;
 
-        // TODO(Teleca): Don't poll signal strength if screen is off
+        // TODO Don't poll signal strength if screen is off
         sendMessageDelayed(msg, POLL_PERIOD_MILLIS);
     }
 
@@ -1108,8 +1080,8 @@
     }
 
 
-    private int radioTechnologyToServiceState(int code) {
-        int retVal = ServiceState.RADIO_TECHNOLOGY_UNKNOWN;
+    private int radioTechnologyToDataServiceState(int code) {
+        int retVal = ServiceState.STATE_OUT_OF_SERVICE;
         switch(code) {
         case 0:
         case 1:
@@ -1118,14 +1090,10 @@
         case 4:
         case 5:
             break;
-        case 6:
-            retVal = ServiceState.RADIO_TECHNOLOGY_1xRTT;
-            break;
-        case 7:
-            retVal = ServiceState.RADIO_TECHNOLOGY_EVDO_0;
-            break;
-        case 8:
-            retVal = ServiceState.RADIO_TECHNOLOGY_EVDO_A;
+        case 6: // RADIO_TECHNOLOGY_1xRTT
+        case 7: // RADIO_TECHNOLOGY_EVDO_0
+        case 8: // RADIO_TECHNOLOGY_EVDO_A
+            retVal = ServiceState.STATE_IN_SERVICE;
             break;
         default:
             Log.e(LOG_TAG, "Wrong radioTechnology code.");
@@ -1435,4 +1403,12 @@
         Log.d(LOG_TAG, "[CdmaServiceStateTracker] " + s);
     }
 
+    public String getMdnNumber() {
+        return mMdn;
+    }
+
+    public String getCdmaMin() {
+         return mMin;
+    }
+
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/EriInfo.java b/telephony/java/com/android/internal/telephony/cdma/EriInfo.java
index 40358c8..5c8e23e 100644
--- a/telephony/java/com/android/internal/telephony/cdma/EriInfo.java
+++ b/telephony/java/com/android/internal/telephony/cdma/EriInfo.java
@@ -23,28 +23,6 @@
     public static final int ROAMING_ICON_MODE_NORMAL    = 0;
     public static final int ROAMING_ICON_MODE_FLASH     = 1;
 
-    /**
-     * TODO(Teleca): These strings appear to be used by the UI
-     * hence they must be changed to resources so they can be
-     * translated to the appropriate language.
-     */
-    public static final String ROAMING_TEXT_0  = "Roaming Indicator On";
-    public static final String ROAMING_TEXT_1  = "Roaming Indicator Off";
-    public static final String ROAMING_TEXT_2  = "Roaming Indicator Flashing";
-    public static final String ROAMING_TEXT_3  = "Out of Neighborhood";
-    public static final String ROAMING_TEXT_4  = "Out of Building";
-    public static final String ROAMING_TEXT_5  = "Roaming - Preferred System";
-    public static final String ROAMING_TEXT_6  = "Roaming - Available System";
-    public static final String ROAMING_TEXT_7  = "Roaming - Alliance Partner";
-    public static final String ROAMING_TEXT_8  = "Roaming - Premium Partner";
-    public static final String ROAMING_TEXT_9  = "Roaming - Full Service Functionality";
-    public static final String ROAMING_TEXT_10 = "Roaming - Partial Service Functionality";
-    public static final String ROAMING_TEXT_11 = "Roaming Banner On";
-    public static final String ROAMING_TEXT_12 = "Roaming Banner Off";
-
-    public static final String SEARCHING_TEXT  = "Searching for Svc.";
-
-
     public int mRoamingIndicator;
     public int mIconIndex;
     public int mIconMode;
diff --git a/telephony/java/com/android/internal/telephony/cdma/EriManager.java b/telephony/java/com/android/internal/telephony/cdma/EriManager.java
index d905e66..0997456 100644
--- a/telephony/java/com/android/internal/telephony/cdma/EriManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/EriManager.java
@@ -28,17 +28,12 @@
 import java.util.HashMap;
 
 /**
- * TODO(Teleca): Please as some comments on how this class is to
- * be used. We've removed Handler as a base class and instead
- * recommend that child classes add a Handler as a member if its
- * needed.
+ * EriManager loads the ERI file definitions and manages the CDMA roaming information.
+ *
  */
-
-
 public final class EriManager {
 
     class EriFile {
-        public static final int MAX_ERI_ENTRIES = 30;
 
         public int mVersionNumber;                      // File version number
         public int mNumberOfEriEntries;                 // Number of entries
@@ -55,7 +50,30 @@
             this.mCallPromptId = new String[] { "", "", "" };
             this.mRoamIndTable = new HashMap<Integer, EriInfo>();
         }
+    }
 
+    class EriDisplayInformation {
+        public int mEriIconIndex;
+        public int mEriIconMode;
+        public String mEriIconText;
+
+        public EriDisplayInformation(int eriIconIndex, int eriIconMode, String eriIconText) {
+            mEriIconIndex = eriIconIndex;
+            mEriIconMode = eriIconMode;
+            mEriIconText = eriIconText;
+        }
+
+//        public void setParameters(int eriIconIndex, int eriIconMode, String eriIconText){
+//            this.mEriIconIndex = eriIconIndex;
+//            this.mEriIconMode = eriIconMode;
+//            this.mEriIconText = eriIconText;
+//        }
+
+        @Override
+        public String toString() {
+            return "EriDisplayInformation: {" + " IconIndex: " + mEriIconIndex + " EriIconMode: "
+                    + mEriIconMode + " EriIconText: " + mEriIconText + " }";
+        }
     }
 
     static final String LOG_TAG = "CDMA";
@@ -67,7 +85,7 @@
     private PhoneBase mPhone;
     private Context mContext;
     private int mEriFileSource = ERI_FROM_XML;
-    private boolean isEriFileLoaded = false;
+    private boolean isEriFileLoaded;
     private EriFile mEriFile;
 
     public EriManager(PhoneBase phone, Context context, int eriFileSource) {
@@ -214,11 +232,205 @@
      * Returns the EriInfo record associated with roamingIndicator
      * or null if the entry is not found
      */
-    public EriInfo getEriInfo(int roamingIndicator) {
+    private EriInfo getEriInfo(int roamingIndicator) {
         if (mEriFile.mRoamIndTable.containsKey(roamingIndicator)) {
             return mEriFile.mRoamIndTable.get(roamingIndicator);
         } else {
             return null;
         }
     }
+
+    private EriDisplayInformation getEriDisplayInformation(int roamInd, int defRoamInd){
+        //int iconIndex = -1;
+        //int iconMode = -1;
+        //String iconText = "ERI text";
+        EriDisplayInformation ret;
+
+        switch (roamInd) {
+        // Handling the standard roaming indicator (non-ERI)
+        case EriInfo.ROAMING_INDICATOR_ON:
+            ret = new EriDisplayInformation(
+                    EriInfo.ROAMING_INDICATOR_ON,
+                    EriInfo.ROAMING_ICON_MODE_NORMAL,
+                    mContext.getText(com.android.internal.R.string.roamingText0).toString());
+            break;
+        
+        case EriInfo.ROAMING_INDICATOR_OFF:
+            ret = new EriDisplayInformation(
+                    EriInfo.ROAMING_INDICATOR_OFF,
+                    EriInfo.ROAMING_ICON_MODE_NORMAL,
+                    mContext.getText(com.android.internal.R.string.roamingText1).toString());
+            break;
+
+        case EriInfo.ROAMING_INDICATOR_FLASH:
+            ret = new EriDisplayInformation(
+                    EriInfo.ROAMING_INDICATOR_FLASH,
+                    EriInfo.ROAMING_ICON_MODE_FLASH,
+                    mContext.getText(com.android.internal.R.string.roamingText2).toString());
+            break;
+
+
+        // Handling the standard ERI
+        case 3:
+            ret = new EriDisplayInformation(
+                    roamInd,
+                    EriInfo.ROAMING_ICON_MODE_NORMAL,
+                    mContext.getText(com.android.internal.R.string.roamingText3).toString());
+            break;
+
+        case 4:
+            ret = new EriDisplayInformation(
+                    roamInd,
+                    EriInfo.ROAMING_ICON_MODE_NORMAL,
+                    mContext.getText(com.android.internal.R.string.roamingText4).toString());
+            break;
+        
+        case 5:
+            ret = new EriDisplayInformation(
+                    roamInd,
+                    EriInfo.ROAMING_ICON_MODE_NORMAL,
+                    mContext.getText(com.android.internal.R.string.roamingText5).toString());
+            break;
+        
+        case 6:
+            ret = new EriDisplayInformation(
+                    roamInd,
+                    EriInfo.ROAMING_ICON_MODE_NORMAL,
+                    mContext.getText(com.android.internal.R.string.roamingText6).toString());
+            break;
+        
+        case 7:
+            ret = new EriDisplayInformation(
+                    roamInd,
+                    EriInfo.ROAMING_ICON_MODE_NORMAL,
+                    mContext.getText(com.android.internal.R.string.roamingText7).toString());
+            break;
+        
+        case 8:
+            ret = new EriDisplayInformation(
+                    roamInd,
+                    EriInfo.ROAMING_ICON_MODE_NORMAL,
+                    mContext.getText(com.android.internal.R.string.roamingText8).toString());
+            break;
+        
+        case 9:
+            ret = new EriDisplayInformation(
+                    roamInd,
+                    EriInfo.ROAMING_ICON_MODE_NORMAL,
+                    mContext.getText(com.android.internal.R.string.roamingText9).toString());
+            break;
+        
+        case 10:
+            ret = new EriDisplayInformation(
+                    roamInd,
+                    EriInfo.ROAMING_ICON_MODE_NORMAL,
+                    mContext.getText(com.android.internal.R.string.roamingText10).toString());
+            break;
+        
+        case 11:
+            ret = new EriDisplayInformation(
+                    roamInd,
+                    EriInfo.ROAMING_ICON_MODE_NORMAL,
+                    mContext.getText(com.android.internal.R.string.roamingText11).toString());
+            break;
+        
+        case 12:
+            ret = new EriDisplayInformation(
+                    roamInd,
+                    EriInfo.ROAMING_ICON_MODE_NORMAL,
+                    mContext.getText(com.android.internal.R.string.roamingText12).toString());
+            break;
+
+        // Handling the non standard Enhanced Roaming Indicator (roamInd > 63)
+        default:
+            if (!isEriFileLoaded) {
+                // ERI file NOT loaded
+                Log.d(LOG_TAG, "ERI File not loaded");
+                if(defRoamInd > 2) {
+                    Log.d(LOG_TAG, "ERI defRoamInd > 2 ...flashing");
+                    ret = new EriDisplayInformation(
+                            EriInfo.ROAMING_INDICATOR_FLASH,
+                            EriInfo.ROAMING_ICON_MODE_FLASH,
+                            mContext.getText(com.android.internal
+                                                            .R.string.roamingText2).toString());
+                } else {
+                    Log.d(LOG_TAG, "ERI defRoamInd <= 2");
+                    switch (defRoamInd) {
+                    case EriInfo.ROAMING_INDICATOR_ON:
+                        ret = new EriDisplayInformation(
+                                EriInfo.ROAMING_INDICATOR_ON,
+                                EriInfo.ROAMING_ICON_MODE_NORMAL,
+                                mContext.getText(com.android.internal
+                                                            .R.string.roamingText0).toString());
+                        break;
+                    
+                    case EriInfo.ROAMING_INDICATOR_OFF:
+                        ret = new EriDisplayInformation(
+                                EriInfo.ROAMING_INDICATOR_OFF,
+                                EriInfo.ROAMING_ICON_MODE_NORMAL,
+                                mContext.getText(com.android.internal
+                                                            .R.string.roamingText1).toString());
+                        break;
+                    
+                    case EriInfo.ROAMING_INDICATOR_FLASH:
+                        ret = new EriDisplayInformation(
+                                EriInfo.ROAMING_INDICATOR_FLASH,
+                                EriInfo.ROAMING_ICON_MODE_FLASH,
+                                mContext.getText(com.android.internal
+                                                            .R.string.roamingText2).toString());
+                        break;
+                        
+                    default:
+                        ret = new EriDisplayInformation(-1, -1, "ERI text");
+                    }
+                }
+            } else {
+                // ERI file loaded
+                Log.d(LOG_TAG, "ERI File loaded");
+                EriInfo eriInfo = getEriInfo(roamInd);
+                EriInfo defEriInfo = getEriInfo(defRoamInd);
+                if (eriInfo == null) {
+                    Log.d(LOG_TAG, "ERI roamInd " + roamInd
+                            + " not found in ERI file ...using defRoamInd " + defRoamInd);
+                    if(defEriInfo == null) {
+                        Log.e(LOG_TAG, "ERI defRoamInd " + defRoamInd
+                                + " not found in ERI file ...on");
+                        ret = new EriDisplayInformation(
+                                EriInfo.ROAMING_INDICATOR_ON,
+                                EriInfo.ROAMING_ICON_MODE_NORMAL,
+                                mContext.getText(com.android.internal
+                                                             .R.string.roamingText0).toString());
+
+                    } else {
+                        Log.d(LOG_TAG, "ERI defRoamInd " + defRoamInd + " found in ERI file");
+                        ret = new EriDisplayInformation(
+                                defEriInfo.mIconIndex,
+                                defEriInfo.mIconMode,
+                                defEriInfo.mEriText);
+                    }
+                } else {
+                    Log.d(LOG_TAG, "ERI roamInd " + roamInd + " found in ERI file");
+                    ret = new EriDisplayInformation(
+                            eriInfo.mIconIndex,
+                            eriInfo.mIconMode,
+                            eriInfo.mEriText);
+                }
+            }
+            break;
+        }
+        Log.d(LOG_TAG, "Displaying ERI " + ret.toString());
+        return ret;
+    }
+
+    public int getCdmaEriIconIndex(int roamInd, int defRoamInd){
+        return getEriDisplayInformation(roamInd, defRoamInd).mEriIconIndex;
+    }
+
+    public int getCdmaEriIconMode(int roamInd, int defRoamInd){
+        return getEriDisplayInformation(roamInd, defRoamInd).mEriIconMode;
+    }
+
+    public String getCdmaEriText(int roamInd, int defRoamInd){
+        return getEriDisplayInformation(roamInd, defRoamInd).mEriIconText;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
index d5b8379..7edd30f 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
@@ -55,11 +55,8 @@
 
     //***** Instance Variables
 
-    private String mImsi;       // TODO(Teleca): to be checked, if this should be removed!
-    private String mMyMobileNumber;
-    private String mSid;        // TODO(Teleca): Unused should this be removed
-    private String mNid;        // TODO(Teleca): Unused should this be removed
-    private String mMin2Min1;
+    String spn;
+    int spnDisplayCondition;
 
     //***** Event Constants
 
@@ -134,19 +131,6 @@
         recordsRequested = false;
     }
 
-    /** Returns null if RUIM is not yet ready */
-    public String getIMSI_M() {
-        return mImsi;
-    }
-
-    public String getMdnNumber() {
-        return mMyMobileNumber;
-    }
-
-    public String getCdmaMin() {
-         return mMin2Min1;
-    }
-
     @Override
     public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete){
         // In CDMA this is Operator/OEM dependent
@@ -171,32 +155,6 @@
         }
     }
 
-    /** 
-     * Returns the 5 or 6 digit MCC/MNC of the operator that
-     *  provided the RUIM card. Returns null of RUIM is not yet ready
-     */
-    public String getRUIMOperatorNumeric() {
-        if (mImsi == null) {
-            return null;
-        }
-
-        if (mncLength != 0) {
-            // Length = length of MCC + length of MNC
-            // TODO: change spec name
-            // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3)
-            return mImsi.substring(0, 3 + mncLength);
-        }
-
-        // Guess the MNC length based on the MCC if we don't
-        // have a valid value in ef[ad]
-
-        int mcc;
-
-        mcc = Integer.parseInt(mImsi.substring(0,3));
-
-        return mImsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc));
-    }
-
     @Override
     public void handleMessage(Message msg) {
         AsyncResult ar;
@@ -223,28 +181,35 @@
             /* IO events */
 
             case EVENT_GET_CDMA_SUBSCRIPTION_DONE:
+                // TODO(Moto):TODO(Teleca): This event was removed by Teleca/QCT
+                // I've left it as it's needed to complete EVENT_OTA_PROVISION_STATUS_CHANGE.
+                // But since various instance variables are removed I've commented
+                // out code that references them. I'm sure this is wrong so
+                // Moto/Teleca/QCT need to come to an agreement. Also see onRuimReady
+                // and onVnReady.
+
                 ar = (AsyncResult)msg.obj;
                 String localTemp[] = (String[])ar.result;
                 if (ar.exception != null) {
                     break;
                 }
                 if(m_ota_commited) {
-                    if(mMyMobileNumber != localTemp[0]) {
+                    //if(mMyMobileNumber != localTemp[0]) {
                         Intent intent = new Intent(TelephonyIntents.ACTION_CDMA_OTA_MDN_CHANGED);
                         intent.putExtra("mdn", localTemp[0]);
                         Log.d(LOG_TAG,"Broadcasting intent MDN Change in OTA ");
                         ActivityManagerNative.broadcastStickyIntent(intent, null);
-                    }
+                    //}
                     m_ota_commited=false;
                 }
-                mMyMobileNumber = localTemp[0];
-                mSid = localTemp[1];
-                mNid = localTemp[2];
-                if (localTemp.length >= 3) { // TODO(Moto): remove when new ril always returns min2_min1
-                   mMin2Min1 = localTemp[3];
-                }
+                //mMyMobileNumber = localTemp[0];
+                //mSid = localTemp[1];
+                //mNid = localTemp[2];
+                //if (localTemp.length >= 3) { // TODO(Moto): remove when new ril always returns min2_min1
+                //   mMin2Min1 = localTemp[3];
+                //}
 
-                Log.d(LOG_TAG, "MDN: " + mMyMobileNumber + " MIN: " + mMin2Min1);
+                //Log.d(LOG_TAG, "MDN: " + mMyMobileNumber + " MIN: " + mMin2Min1);
 
             break;
 
@@ -350,14 +315,14 @@
                 RuimCard.INTENT_VALUE_ICC_READY, null);
 
         fetchRuimRecords();
-
-        phone.mCM.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE));
-
+        
+        // TODO(Moto): TODO(Teleca): Work out how to do CDMA subscription
+        // phone.mCM.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE));
     }
 
     private void onNvReady() {
-        phone.mCM.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE));
-
+        // TODO(Moto): TODO(Teleca): Work out how to do CDMA subscription
+        // phone.mCM.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE));        
     }
 
     private void fetchRuimRecords() {
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index b2083ed..9152559 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -356,39 +356,6 @@
         return privateGetSubmitPdu(destAddr, statusReportRequested, uData);
     }
 
-    static class PduParser {
-
-        PduParser() {
-        }
-
-        /**
-         * Parses an SC timestamp and returns a currentTimeMillis()-style
-         * timestamp
-         */
-        static long getSCTimestampMillis(byte[] timestamp) {
-            // TP-Service-Centre-Time-Stamp
-            int year = IccUtils.beBcdByteToInt(timestamp[0]);
-            int month = IccUtils.beBcdByteToInt(timestamp[1]);
-            int day = IccUtils.beBcdByteToInt(timestamp[2]);
-            int hour = IccUtils.beBcdByteToInt(timestamp[3]);
-            int minute = IccUtils.beBcdByteToInt(timestamp[4]);
-            int second = IccUtils.beBcdByteToInt(timestamp[5]);
-
-            Time time = new Time(Time.TIMEZONE_UTC);
-
-            // C.S0015-B v2.0, 4.5.4: range is 1996-2095
-            time.year = year >= 96 ? year + 1900 : year + 2000;
-            time.month = month - 1;
-            time.monthDay = day;
-            time.hour = hour;
-            time.minute = minute;
-            time.second = second;
-
-            return time.toMillis(true);
-        }
-
-    }
-
     /**
      * Note: This function is a GSM specific functionality which is not supported in CDMA mode.
      */
@@ -557,8 +524,8 @@
                     + originatingAddress.address);
         }
 
-        if (mBearerData.timeStamp != null) {
-                scTimeMillis = PduParser.getSCTimestampMillis(mBearerData.timeStamp);
+        if (mBearerData.msgCenterTimeStamp != null) {
+            scTimeMillis = mBearerData.msgCenterTimeStamp.toMillis(true);
         }
 
         if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);
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 05c8c9d..ab65b0a 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -20,6 +20,9 @@
 
 import android.telephony.SmsMessage;
 
+import android.text.format.Time;
+
+import com.android.internal.telephony.IccUtils;
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.cdma.sms.UserData;
@@ -38,15 +41,16 @@
     /**
      * Bearer Data Subparameter Indentifiers
      * (See 3GPP2 C.S0015-B, v2.0, table 4.5-1)
+     * NOTE: Commented subparameter types are not implemented.
      */
     private final static byte SUBPARAM_MESSAGE_IDENTIFIER               = 0x00;
     private final static byte SUBPARAM_USER_DATA                        = 0x01;
     private final static byte SUBPARAM_USER_REPONSE_CODE                = 0x02;
     private final static byte SUBPARAM_MESSAGE_CENTER_TIME_STAMP        = 0x03;
-    //private final static byte SUBPARAM_VALIDITY_PERIOD_ABSOLUTE         = 0x04;
-    //private final static byte SUBPARAM_VALIDITY_PERIOD_RELATIVE         = 0x05;
-    //private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE  = 0x06;
-    //private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE  = 0x07;
+    private final static byte SUBPARAM_VALIDITY_PERIOD_ABSOLUTE         = 0x04;
+    private final static byte SUBPARAM_VALIDITY_PERIOD_RELATIVE         = 0x05;
+    private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE  = 0x06;
+    private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE  = 0x07;
     private final static byte SUBPARAM_PRIORITY_INDICATOR               = 0x08;
     private final static byte SUBPARAM_PRIVACY_INDICATOR                = 0x09;
     private final static byte SUBPARAM_REPLY_OPTION                     = 0x0A;
@@ -56,7 +60,7 @@
     private final static byte SUBPARAM_CALLBACK_NUMBER                  = 0x0E;
     private final static byte SUBPARAM_MESSAGE_DISPLAY_MODE             = 0x0F;
     //private final static byte SUBPARAM_MULTIPLE_ENCODING_USER_DATA      = 0x10;
-    //private final static byte SUBPARAM_MESSAGE_DEPOSIT_INDEX            = 0x11;
+    private final static byte SUBPARAM_MESSAGE_DEPOSIT_INDEX            = 0x11;
     //private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA    = 0x12;
     //private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS = 0x13;
     private final static byte SUBPARAM_MESSAGE_STATUS                   = 0x14;
@@ -205,23 +209,94 @@
      */
     public UserData userData;
 
-    //public UserResponseCode userResponseCode;
+    /**
+     * The User Response Code subparameter is used in the SMS User
+     * Acknowledgment Message to respond to previously received short
+     * messages. This message center-specific element carries the
+     * identifier of a predefined response. (See 3GPP2 C.S.0015-B, v2,
+     * 4.5.3)
+     */
+    public boolean userResponseCodeSet = false;
+    public int userResponseCode;
 
     /**
      * 6-byte-field, see 3GPP2 C.S0015-B, v2, 4.5.4
-     * year, month, day, hours, minutes, seconds;
      */
-    public byte[] timeStamp;
+    public static class TimeStamp extends Time {
 
-    //public SmsTime validityPeriodAbsolute;
-    //public SmsRelTime validityPeriodRelative;
-    //public SmsTime deferredDeliveryTimeAbsolute;
-    //public SmsRelTime deferredDeliveryTimeRelative;
+        public TimeStamp() {
+            super(Time.TIMEZONE_UTC);
+        }
+
+        public static TimeStamp fromByteArray(byte[] data) {
+            TimeStamp ts = new TimeStamp();
+            // C.S0015-B v2.0, 4.5.4: range is 1996-2095
+            int year = IccUtils.beBcdByteToInt(data[0]);
+            if (year > 99 || year < 0) return null;
+            ts.year = year >= 96 ? year + 1900 : year + 2000;
+            int month = IccUtils.beBcdByteToInt(data[1]);
+            if (month < 1 || month > 12) return null;
+            ts.month = month - 1;
+            int day = IccUtils.beBcdByteToInt(data[2]);
+            if (day < 1 || day > 31) return null;
+            ts.monthDay = day;
+            int hour = IccUtils.beBcdByteToInt(data[3]);
+            if (hour < 0 || hour > 23) return null;
+            ts.hour = hour;
+            int minute = IccUtils.beBcdByteToInt(data[4]);
+            if (minute < 0 || minute > 59) return null;
+            ts.minute = minute;
+            int second = IccUtils.beBcdByteToInt(data[5]);
+            if (second < 0 || second > 59) return null;
+            ts.second = second;
+            return ts;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("TimeStamp ");
+            builder.append("{ year=" + year);
+            builder.append(", month=" + month);
+            builder.append(", day=" + monthDay);
+            builder.append(", hour=" + hour);
+            builder.append(", minute=" + minute);
+            builder.append(", second=" + second);
+            builder.append(" }");
+            return builder.toString();
+        }
+    }
+
+    public TimeStamp msgCenterTimeStamp;
+    public TimeStamp validityPeriodAbsolute;
+    public TimeStamp deferredDeliveryTimeAbsolute;
 
     /**
-     * Reply Option
-     * 1-bit values which indicate whether SMS acknowledgment is requested or not.
-     * (See 3GPP2 C.S0015-B, v2, 4.5.11)
+     * Relative time is specified as one byte, the value of which
+     * falls into a series of ranges, as specified below.  The idea is
+     * that shorter time intervals allow greater precision -- the
+     * value means minutes from zero until the MINS_LIMIT (inclusive),
+     * upon which it means hours until the HOURS_LIMIT, and so
+     * forth. (See 3GPP2 C.S0015-B, v2, 4.5.6-1)
+     */
+    public static final int RELATIVE_TIME_MINS_LIMIT      = 143;
+    public static final int RELATIVE_TIME_HOURS_LIMIT     = 167;
+    public static final int RELATIVE_TIME_DAYS_LIMIT      = 196;
+    public static final int RELATIVE_TIME_WEEKS_LIMIT     = 244;
+    public static final int RELATIVE_TIME_INDEFINITE      = 245;
+    public static final int RELATIVE_TIME_NOW             = 246;
+    public static final int RELATIVE_TIME_MOBILE_INACTIVE = 247;
+    public static final int RELATIVE_TIME_RESERVED        = 248;
+
+    public boolean validityPeriodRelativeSet;
+    public int validityPeriodRelative;
+    public boolean deferredDeliveryTimeRelativeSet;
+    public int deferredDeliveryTimeRelative;
+
+    /**
+     * The Reply Option subparameter contains 1-bit values which
+     * indicate whether SMS acknowledgment is requested or not.  (See
+     * 3GPP2 C.S0015-B, v2, 4.5.11)
      */
     public boolean userAckReq;
     public boolean deliveryAckReq;
@@ -229,14 +304,28 @@
     public boolean reportReq;
 
     /**
-     * The number of Messages element (8-bit value) is a decimal number in the 0 to 99 range
-     * representing the number of messages stored at the Voice Mail System. This element is
-     * used by the Voice Mail Notification service.
-     * (See 3GPP2 C.S0015-B, v2, 4.5.12)
+     * The Number of Messages subparameter (8-bit value) is a decimal
+     * number in the 0 to 99 range representing the number of messages
+     * stored at the Voice Mail System. This element is used by the
+     * Voice Mail Notification service.  (See 3GPP2 C.S0015-B, v2,
+     * 4.5.12)
      */
     public int numberOfMessages;
 
     /**
+     * The Message Deposit Index subparameter is assigned by the
+     * message center as a unique index to the contents of the User
+     * Data subparameter in each message sent to a particular mobile
+     * station. The mobile station, when replying to a previously
+     * received short message which included a Message Deposit Index
+     * subparameter, may include the Message Deposit Index of the
+     * received message to indicate to the message center that the
+     * original contents of the message are to be included in the
+     * reply.  (See 3GPP2 C.S0015-B, v2, 4.5.18)
+     */
+    public int depositIndex;
+
+    /**
      * 4-bit or 8-bit value that indicates the number to be dialed in reply to a
      * received SMS message.
      * (See 3GPP2 C.S0015-B, v2, 4.5.15)
@@ -262,14 +351,23 @@
         builder.append(", language=" + (languageIndicatorSet ? language : "unset"));
         builder.append(", errorClass=" + (messageStatusSet ? errorClass : "unset"));
         builder.append(", msgStatus=" + (messageStatusSet ? messageStatus : "unset"));
-        builder.append(", timeStamp=" +
-                ((timeStamp != null) ? HexDump.toHexString(timeStamp) : "unset"));
+        builder.append(", msgCenterTimeStamp=" +
+                ((msgCenterTimeStamp != null) ? msgCenterTimeStamp : "unset"));
+        builder.append(", validityPeriodAbsolute=" +
+                ((validityPeriodAbsolute != null) ? validityPeriodAbsolute : "unset"));
+        builder.append(", validityPeriodRelative=" +
+                ((validityPeriodRelativeSet) ? validityPeriodRelative : "unset"));
+        builder.append(", deferredDeliveryTimeAbsolute=" +
+                ((deferredDeliveryTimeAbsolute != null) ? deferredDeliveryTimeAbsolute : "unset"));
+        builder.append(", deferredDeliveryTimeRelative=" +
+                ((deferredDeliveryTimeRelativeSet) ? deferredDeliveryTimeRelative : "unset"));
         builder.append(", userAckReq=" + userAckReq);
         builder.append(", deliveryAckReq=" + deliveryAckReq);
         builder.append(", readAckReq=" + readAckReq);
         builder.append(", reportReq=" + reportReq);
         builder.append(", numberOfMessages=" + numberOfMessages);
         builder.append(", callbackNumber=" + callbackNumber);
+        builder.append(", depositIndex=" + depositIndex);
         builder.append(", hasUserDataHeader=" + hasUserDataHeader);
         builder.append(", userData=" + userData);
         builder.append(" }");
@@ -518,11 +616,11 @@
         outStream.write(8, bData.numberOfMessages);
     }
 
-    private static void encodeMsgCenterTimeStamp(BearerData bData, BitwiseOutputStream outStream)
+    private static void encodeValidityPeriodRel(BearerData bData, BitwiseOutputStream outStream)
         throws BitwiseOutputStream.AccessException
     {
-        outStream.write(8, 6);
-        outStream.writeByteArray(6 * 8, bData.timeStamp);
+        outStream.write(8, 1);
+        outStream.write(8, bData.validityPeriodRelative);
     }
 
     private static void encodePrivacyIndicator(BearerData bData, BitwiseOutputStream outStream)
@@ -595,9 +693,9 @@
                 outStream.write(8, SUBPARAM_NUMBER_OF_MESSAGES);
                 encodeMsgCount(bData, outStream);
             }
-            if (bData.timeStamp != null) {
-                outStream.write(8, SUBPARAM_MESSAGE_CENTER_TIME_STAMP);
-                encodeMsgCenterTimeStamp(bData, outStream);
+            if (bData.validityPeriodRelativeSet) {
+                outStream.write(8, SUBPARAM_VALIDITY_PERIOD_RELATIVE);
+                encodeValidityPeriodRel(bData, outStream);
             }
             if (bData.privacyIndicatorSet) {
                 outStream.write(8, SUBPARAM_PRIVACY_INDICATOR);
@@ -791,6 +889,15 @@
         bData.numberOfMessages = inStream.read(8);
     }
 
+    private static void decodeDepositIndex(BearerData bData, BitwiseInputStream inStream)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        if (inStream.read(8) != 2) {
+            throw new CodingException("MESSAGE_DEPOSIT_INDEX subparam size incorrect");
+        }
+        bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
+    }
+
     private static String decodeDtmfSmsAddress(byte[] rawData, int numFields)
         throws CodingException
     {
@@ -863,14 +970,51 @@
         bData.messageStatusSet = true;
     }
 
-    private static void decodeMsgCenterTimeStamp(BearerData bData,
-                                                 BitwiseInputStream inStream)
+    private static void decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
         if (inStream.read(8) != 6) {
             throw new CodingException("MESSAGE_CENTER_TIME_STAMP subparam size incorrect");
         }
-        bData.timeStamp = inStream.readByteArray(6 * 8);
+        bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
+    }
+
+    private static void decodeValidityAbs(BearerData bData, BitwiseInputStream inStream)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        if (inStream.read(8) != 6) {
+            throw new CodingException("VALIDITY_PERIOD_ABSOLUTE subparam size incorrect");
+        }
+        bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
+    }
+
+    private static void decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        if (inStream.read(8) != 6) {
+            throw new CodingException("DEFERRED_DELIVERY_TIME_ABSOLUTE subparam size incorrect");
+        }
+        bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
+    }
+
+    private static void decodeValidityRel(BearerData bData, BitwiseInputStream inStream)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        if (inStream.read(8) != 1) {
+            throw new CodingException("VALIDITY_PERIOD_RELATIVE subparam size incorrect");
+        }
+        bData.deferredDeliveryTimeRelative = inStream.read(8);
+        bData.deferredDeliveryTimeRelativeSet = true;
+    }
+
+    private static void decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        if (inStream.read(8) != 1) {
+            throw new CodingException("DEFERRED_DELIVERY_TIME_RELATIVE subparam size incorrect");
+        }
+        bData.validityPeriodRelative = inStream.read(8);
+        bData.validityPeriodRelativeSet = true;
     }
 
     private static void decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream)
@@ -927,6 +1071,16 @@
         bData.alertIndicatorSet = true;
     }
 
+    private static void decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        if (inStream.read(8) != 1) {
+            throw new CodingException("USER_REPONSE_CODE subparam size incorrect");
+        }
+        bData.userResponseCode = inStream.read(8);
+        bData.userResponseCodeSet = true;
+    }
+
     /**
      * Create BearerData object from serialized representation.
      * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
@@ -955,6 +1109,9 @@
                 case SUBPARAM_USER_DATA:
                     decodeUserData(bData, inStream);
                     break;
+                case SUBPARAM_USER_REPONSE_CODE:
+                    decodeUserResponseCode(bData, inStream);
+                    break;
                 case SUBPARAM_REPLY_OPTION:
                     decodeReplyOption(bData, inStream);
                     break;
@@ -970,6 +1127,18 @@
                 case SUBPARAM_MESSAGE_CENTER_TIME_STAMP:
                     decodeMsgCenterTimeStamp(bData, inStream);
                     break;
+                case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE:
+                    decodeValidityAbs(bData, inStream);
+                    break;
+                case SUBPARAM_VALIDITY_PERIOD_RELATIVE:
+                    decodeValidityRel(bData, inStream);
+                    break;
+                case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE:
+                    decodeDeferredDeliveryAbs(bData, inStream);
+                    break;
+                case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE:
+                    decodeDeferredDeliveryRel(bData, inStream);
+                    break;
                 case SUBPARAM_PRIVACY_INDICATOR:
                     decodePrivacyIndicator(bData, inStream);
                     break;
@@ -985,6 +1154,9 @@
                 case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY:
                     decodeMsgDeliveryAlert(bData, inStream);
                     break;
+                case SUBPARAM_MESSAGE_DEPOSIT_INDEX:
+                    decodeDepositIndex(bData, inStream);
+                    break;
                 default:
                     throw new CodingException("unsupported bearer data subparameter ("
                                               + subparamId + ")");
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
index 440debb..917ec9d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
@@ -20,23 +20,31 @@
 import com.android.internal.util.HexDump;
 
 public class CdmaSmsAddress extends SmsAddress {
+
     /**
-     * digit mode indicators
-     * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
+     * Digit Mode Indicator is a 1-bit value that indicates whether
+     * the address digits are 4-bit DTMF codes or 8-bit codes.  (See
+     * 3GPP2 C.S0015-B, v2, 3.4.3.3)
      */
     static public final int DIGIT_MODE_4BIT_DTMF              = 0x00;
     static public final int DIGIT_MODE_8BIT_CHAR              = 0x01;
 
+    public byte digitMode;
+
     /**
-     * number mode indicators
-     * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
+     * Number Mode Indicator is 1-bit value that indicates whether the
+     * address type is a data network address or not.  (See 3GPP2
+     * C.S0015-B, v2, 3.4.3.3)
      */
     static public final int NUMBER_MODE_NOT_DATA_NETWORK      = 0x00;
     static public final int NUMBER_MODE_DATA_NETWORK          = 0x01;
 
+    public byte numberMode;
+
     /**
-     *  number types for data networks
-     *  (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
+     * Number Types for data networks.
+     * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
+     * NOTE: value is stored in the parent class ton field.
      */
     static public final int TON_UNKNOWN                   = 0x00;
     static public final int TON_INTERNATIONAL_OR_IP       = 0x01;
@@ -48,65 +56,51 @@
     static public final int TON_RESERVED                  = 0x07;
 
     /**
-     *  maximum lengths for fields as defined in ril_cdma_sms.h
+     * Maximum lengths for fields as defined in ril_cdma_sms.h.
      */
     static public final int SMS_ADDRESS_MAX          =  36;
     static public final int SMS_SUBADDRESS_MAX       =  36;
 
     /**
-     *  Supported numbering plan identification
-     *  (See C.S005-D, v1.0, table 2.7.1.3.2.4-3)
-     */
-    static public final int NUMBERING_PLAN_UNKNOWN           = 0x0;
-    static public final int NUMBERING_PLAN_ISDN_TELEPHONY    = 0x1;
-    //static protected final int NUMBERING_PLAN_DATA              = 0x3;
-    //static protected final int NUMBERING_PLAN_TELEX             = 0x4;
-    //static protected final int NUMBERING_PLAN_PRIVATE           = 0x9;
-
-    /**
-     * 1-bit value that indicates whether the address digits are 4-bit DTMF codes
-     * or 8-bit codes.
-     * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
-     */
-    public byte digitMode;
-
-    /**
-     * 1-bit value that indicates whether the address type is a data network address or not.
-     * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
-     */
-    public byte numberMode;
-
-    // use parent class member ton instead public byte numberType;
-
-    /**
-     * 0 or 4-bit value that indicates which numbering plan identification is set.
-     * (See 3GPP2, C.S0015-B, v2, 3.4.3.3 and C.S005-D, table2.7.1.3.2.4-3)
-     */
-    public byte numberPlan;
-
-    /**
      * This field shall be set to the number of address digits
      * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
      */
     public byte numberOfDigits;
 
-    // use parent class member orig_bytes instead of public byte[] digits;
+    /**
+     * Numbering Plan identification is a 0 or 4-bit value that
+     * indicates which numbering plan identification is set.  (See
+     * 3GPP2, C.S0015-B, v2, 3.4.3.3 and C.S005-D, table2.7.1.3.2.4-3)
+     */
+    static public final int NUMBERING_PLAN_UNKNOWN           = 0x0;
+    static public final int NUMBERING_PLAN_ISDN_TELEPHONY    = 0x1;
+    //static protected final int NUMBERING_PLAN_DATA              = 0x3;
+    //static protected final int NUMBERING_PLAN_TELEX             = 0x4;
+    //static protected final int NUMBERING_PLAN_PRIVATE           = 0x9;
 
-    // Constructor
+    public byte numberPlan;
+
+    /**
+     * NOTE: the parsed string address and the raw byte array values
+     * are stored in the parent class address and origBytes fields,
+     * respectively.
+     */
+
     public CdmaSmsAddress(){
     }
 
     @Override
     public String toString() {
         StringBuilder builder = new StringBuilder();
-        builder.append("CdmaSmsAddress:\n");
-        builder.append("  digitMode: " + digitMode + "\n");
-        builder.append("  numberMode: " + numberMode + "\n");
-        builder.append("  numberPlan: " + numberPlan + "\n");
-        builder.append("  numberOfDigits: " + numberOfDigits + "\n");
-        builder.append("  ton: " + ton + "\n");
-        builder.append("  address: " + address + "\n");
-        builder.append("  origBytes: " + HexDump.toHexString(origBytes) + "\n");
+        builder.append("CdmaSmsAddress ");
+        builder.append("{ digitMode=" + digitMode);
+        builder.append(", numberMode=" + numberMode);
+        builder.append(", numberPlan=" + numberPlan);
+        builder.append(", numberOfDigits=" + numberOfDigits);
+        builder.append(", ton=" + ton);
+        builder.append(", address=" + address);
+        builder.append(", origBytes=" + HexDump.toHexString(origBytes));
+        builder.append(" }");
         return builder.toString();
     }
 
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 1d918a3..3f43502 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -28,10 +28,9 @@
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.NetworkInfo;
-import android.net.wifi.WifiManager;
 import android.net.Uri;
+import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
-import android.os.Handler;
 import android.os.INetStatService;
 import android.os.Message;
 import android.os.RemoteException;
@@ -42,7 +41,6 @@
 import android.provider.Checkin;
 import android.provider.Settings;
 import android.provider.Telephony;
-import android.provider.Settings.SettingNotFoundException;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.telephony.gsm.GsmCellLocation;
@@ -52,11 +50,10 @@
 
 import com.android.internal.telephony.DataCallState;
 import com.android.internal.telephony.DataConnection;
-import com.android.internal.telephony.DataConnection.FailCause;
 import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneProxy;
 import com.android.internal.telephony.TelephonyEventLog;
+import com.android.internal.telephony.DataConnection.FailCause;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -151,7 +148,6 @@
 
     private static final int POLL_PDP_MILLIS = 5 * 1000;
 
-    //WINK:TODO: Teleca, is this really gsm specific, what about CDMA?
     private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect";
     private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason";
 
@@ -178,9 +174,12 @@
 
                 String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
                 if (state == State.FAILED) {
-                    cleanUpConnection(false, reason);
+                    Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
+                    msg.arg1 = 0; // tearDown is false
+                    msg.obj = (String) reason;
+                    sendMessage(msg);
                 }
-                trySetupData(reason);
+                sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));
             } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                 final android.net.NetworkInfo networkInfo = (NetworkInfo)
                         intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
@@ -261,7 +260,7 @@
         ((GSMPhone) phone).mSST.unregisterForRoamingOff(this);
         ((GSMPhone) phone).mSST.unregisterForPsRestrictedEnabled(this);
         ((GSMPhone) phone).mSST.unregisterForPsRestrictedDisabled(this);
-        
+
         phone.getContext().unregisterReceiver(this.mIntentReceiver);
         phone.getContext().getContentResolver().unregisterContentObserver(this.apnObserver);
 
@@ -363,7 +362,7 @@
      * The APN of the specified type is no longer needed. Ensure that if
      * use of the default APN has not been explicitly disabled, we are connected
      * to the default APN.
-     * @param type the APN type. The only valid values are currently 
+     * @param type the APN type. The only valid values are currently
      * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}.
      * @return
      */
@@ -411,7 +410,7 @@
         if (((GSMPhone) phone).mSIMRecords.getRecordsLoaded() &&
                 ((GSMPhone) phone).mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE &&
                 (!roaming || getDataOnRoamingEnabled()) &&
-            !mIsWifiConnected && 
+            !mIsWifiConnected &&
             !mIsPsRestricted ) {
             return (state == State.CONNECTED);
         }
@@ -495,14 +494,6 @@
             return true;
         }
     }
-    
-    /**
-     * Simply tear down data connections due to radio off 
-     * and don't setup again.
-     */
-    public void cleanConnectionBeforeRadioOff() {
-        cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF);
-    }
 
     /**
      * Report the current state of data connectivity (enabled or disabled) for
@@ -567,7 +558,7 @@
         if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason));
 
         Log.d(LOG_TAG, "[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted);
-        
+
         if (phone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
             // FIXME  this can be improved
@@ -665,7 +656,7 @@
          * IDLE before the code below runs. If we didn't check
          * for that, future calls to trySetupData would fail,
          * and we would never get out of the DISCONNECTING state.
-         */ 
+         */
         if (!tearDown) {
             setState(State.IDLE);
             phone.notifyDataConnection(reason);
@@ -1062,7 +1053,7 @@
             }
 
             int watchdogTrigger = Settings.Gservices.getInt(mResolver,
-                    Settings.Gservices.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, 
+                    Settings.Gservices.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
                     NUMBER_SENT_PACKETS_OF_HANG);
 
             if (sentSinceLastRecv >= watchdogTrigger) {
@@ -1085,7 +1076,7 @@
 
                     // Slow down the poll interval to let things happen
                     netStatPollPeriod = Settings.Gservices.getInt(mResolver,
-                            Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS, 
+                            Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS,
                             POLL_NETSTAT_SLOW_MILLIS);
                 } else {
                     if (DBG) log("Sent " + String.valueOf(sentSinceLastRecv) +
@@ -1118,7 +1109,7 @@
             }
         }
     };
-    
+
     private void runPingTest () {
         int status = -1;
         try {
@@ -1167,19 +1158,19 @@
     /**
      * Return true if data connection need to be setup after disconnected due to
      * reason.
-     * 
+     *
      * @param reason the reason why data is disconnected
-     * @return true if try setup data connection is need for this reason 
+     * @return true if try setup data connection is need for this reason
      */
     private boolean retryAfterDisconnected(String reason) {
         boolean retry = true;
-        
+
         if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ||
-             Phone.REASON_DATA_DISABLED.equals(reason) ) { 
+             Phone.REASON_DATA_DISABLED.equals(reason) ) {
             retry = false;
         }
         return retry;
-    }   
+    }
 
     private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) {
         if (state == State.FAILED) {
@@ -1332,7 +1323,7 @@
             cause = (PdpConnection.FailCause) (ar.result);
             if(DBG) log("PDP setup failed " + cause);
                     // Log this failure to the Event Logs.
-            if (cause == PdpConnection.FailCause.BAD_APN || 
+            if (cause == PdpConnection.FailCause.BAD_APN ||
                     cause == PdpConnection.FailCause.BAD_PAP_SECRET ||
                     cause == PdpConnection.FailCause.BARRED ||
                     cause == PdpConnection.FailCause.RADIO_ERROR_RETRY ||
@@ -1344,7 +1335,7 @@
                 if (loc != null) cid = loc.getCid();
 
                 EventLog.List val = new EventLog.List(
-                        cause.ordinal(), cid, 
+                        cause.ordinal(), cid,
                         TelephonyManager.getDefault().getNetworkType());
                 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_PDP_SETUP_FAIL, val);
             }
@@ -1417,6 +1408,10 @@
         }
     }
 
+    protected void onCleanUpConnection(boolean tearDown, String reason) {
+        cleanUpConnection(tearDown, reason);
+    }
+
     private boolean tryNextApn(FailCause cause) {
         return (cause != FailCause.RADIO_NOT_AVAILABLE)
                 && (cause != FailCause.RADIO_OFF)
@@ -1577,7 +1572,7 @@
         ContentResolver resolver = phone.getContext().getContentResolver();
         resolver.delete(PREFERAPN_URI, null, null);
 
-        if (pos >= 0) {            
+        if (pos >= 0) {
             ContentValues values = new ContentValues();
             values.put(APN_ID, pos);
             resolver.insert(PREFERAPN_URI, values);
@@ -1590,7 +1585,7 @@
         }
 
         Cursor cursor = phone.getContext().getContentResolver().query(
-                PREFERAPN_URI, new String[] { "_id", "name", "apn" }, 
+                PREFERAPN_URI, new String[] { "_id", "name", "apn" },
                 null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
 
         if (cursor != null) {
@@ -1674,9 +1669,9 @@
                  * PDP context and notify us with PDP_CONTEXT_CHANGED.
                  * But we should stop the network polling and prevent reset PDP.
                  */
-                Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted); 
+                Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
                 stopNetStatPoll();
-                mIsPsRestricted = true; 
+                mIsPsRestricted = true;
                 break;
 
             case EVENT_PS_RESTRICT_DISABLED:
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 7a4ea64..066f782 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -16,7 +16,13 @@
 
 package com.android.internal.telephony.gsm;
 
-import com.android.internal.telephony.Phone;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ALPHA;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISROAMING;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_NUMERIC;
 import android.app.AlarmManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -50,21 +56,11 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.IccCard;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneProxy;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyEventLog;
 import com.android.internal.telephony.TelephonyIntents;
 
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ALPHA;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISROAMING;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_NUMERIC;
-
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Date;
@@ -75,13 +71,6 @@
  */
 final class GsmServiceStateTracker extends ServiceStateTracker {
 
-    /**
-     * TODO(Teleca): John Huang asks: Will you be adding handling of
-     * "reason for registration denied in EVENT_POLL_STATE_REGISTRATION?
-     * I see some handling of this in CdmaServiceStateTracker, but as I
-     * understand it this field was added at the request of a GSM carrier.
-     */
-
     //***** Instance Variables
     GSMPhone phone;
     GsmCellLocation cellLoc;
@@ -548,7 +537,10 @@
                         (dcTracker.getAnyDataEnabled() ? 1 : 0) );
                 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_DATA_STATE_RADIO_OFF, val);
             }
-            dcTracker.cleanConnectionBeforeRadioOff();
+            Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION);
+            msg.arg1 = 1; // tearDown is true
+            msg.obj = GSMPhone.REASON_RADIO_TURNED_OFF;
+            sendMessage(msg);
 
             // poll data state up to 15 times, with a 100ms delay
             // totaling 1.5 sec. Normal data disable action will finish in 100ms.
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index bfdd8a7..6435be5 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -637,8 +637,6 @@
         /**
          * Returns an object representing the user data headers
          *
-         * @return an object representing the user data headers
-         *
          * {@hide}
          */
         SmsHeader getUserDataHeader() {
diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java b/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java
index 3de14f0..8f0addc 100644
--- a/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java
+++ b/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java
@@ -184,9 +184,6 @@
         mCmdIf.unSetOnStkCallSetUp(this);
 
         this.removeCallbacksAndMessages(null);
-
-        //removing instance
-        sInstance = null;
     }
 
     protected void finalize() {
@@ -450,7 +447,7 @@
     }
 
     /**
-     * Used for instantiating the Service from the GsmPhone constructor.
+     * Used for instantiating/updating the Service from the GsmPhone constructor.
      *
      * @param ci CommandsInterface object
      * @param sr SIMRecords object
diff --git a/test-runner/android/test/mock/MockContentProvider.java b/test-runner/android/test/mock/MockContentProvider.java
index 757c72d..74f86d8 100644
--- a/test-runner/android/test/mock/MockContentProvider.java
+++ b/test-runner/android/test/mock/MockContentProvider.java
@@ -34,6 +34,7 @@
 import android.os.ParcelFileDescriptor;
 
 import java.io.FileNotFoundException;
+import java.util.ArrayList;
 
 /**
  * Mock implementation of IContentProvider that does nothing.  All methods are non-functional and 
@@ -91,7 +92,8 @@
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
-    public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations) throws RemoteException, OperationApplicationException {
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
diff --git a/test-runner/android/test/mock/MockContext.java b/test-runner/android/test/mock/MockContext.java
index bd39a14..e733dd1 100644
--- a/test-runner/android/test/mock/MockContext.java
+++ b/test-runner/android/test/mock/MockContext.java
@@ -386,12 +386,4 @@
             throws PackageManager.NameNotFoundException {
         throw new UnsupportedOperationException();
     }
-
-    /**
-     * @hide
-     */
-    @Override
-    public float getApplicationScale() {
-        throw new UnsupportedOperationException();
-    }
 }
diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
index d68d78c..f98a251 100644
--- a/test-runner/android/test/mock/MockPackageManager.java
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -389,6 +389,16 @@
         throw new UnsupportedOperationException();
     }
     
+    /**
+     * @hide - to match hiding in superclass
+     */
+    @Override
+    public void replacePreferredActivity(IntentFilter filter,
+            int match, ComponentName[] set, ComponentName activity) {
+        throw new UnsupportedOperationException();
+    }
+
+
     @Override
     public void clearPackagePreferredActivities(String packageName) {
         throw new UnsupportedOperationException();
diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml
index 843d844..fd6e6d8 100644
--- a/tests/AndroidTests/AndroidManifest.xml
+++ b/tests/AndroidTests/AndroidManifest.xml
@@ -206,6 +206,12 @@
             <meta-data android:name="com.android.unit_tests.reference" android:resource="@xml/metadata" />
         </provider>
 
+        <!-- Application components used for content tests -->
+        <provider android:name=".content.MemoryFileProvider"
+                android:authorities="com.android.unit_tests.content.MemoryFileProvider"
+                android:process=":MemoryFileProvider">
+        </provider>
+
         <!-- Application components used for os tests -->
 
         <service android:name=".os.MessengerService"
diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
index 75fd157..16aca4d 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
@@ -33,7 +33,7 @@
 import android.util.Log;
 
 public class CdmaSmsTest extends AndroidTestCase {
-    private final static String LOG_TAG = "Cdma_Sms_Test";
+    private final static String LOG_TAG = "CDMA";
 
     @SmallTest
     public void testUserData7bitGsm() throws Exception {
@@ -136,6 +136,100 @@
     }
 
     @SmallTest
+    public void testMonolithicOne() throws Exception {
+        String pdu = "0003200010010410168d2002010503060812011101590501c706069706180000000701c108" +
+                "01c00901800a01e00b01030c01c00d01070e05039acc13880f018011020566";
+        BearerData bearerData = BearerData.decode(HexDump.hexStringToByteArray(pdu));
+        assertEquals(bearerData.messageType, BearerData.MESSAGE_TYPE_SUBMIT);
+        assertEquals(bearerData.messageId, 1);
+        assertEquals(bearerData.priority, BearerData.PRIORITY_EMERGENCY);
+        assertEquals(bearerData.privacy, BearerData.PRIVACY_CONFIDENTIAL);
+        assertEquals(bearerData.userAckReq, true);
+        assertEquals(bearerData.readAckReq, true);
+        assertEquals(bearerData.deliveryAckReq, true);
+        assertEquals(bearerData.reportReq, false);
+        assertEquals(bearerData.numberOfMessages, 3);
+        assertEquals(bearerData.alert, BearerData.ALERT_HIGH_PRIO);
+        assertEquals(bearerData.language, BearerData.LANGUAGE_HEBREW);
+        assertEquals(bearerData.callbackNumber.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF);
+        assertEquals(bearerData.callbackNumber.numberMode,
+                     CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK);
+        assertEquals(bearerData.callbackNumber.ton, CdmaSmsAddress.TON_UNKNOWN);
+        assertEquals(bearerData.callbackNumber.numberPlan, CdmaSmsAddress.NUMBERING_PLAN_UNKNOWN);
+        assertEquals(bearerData.callbackNumber.numberOfDigits, 7);
+        assertEquals(bearerData.callbackNumber.address, "3598271");
+        assertEquals(bearerData.displayMode, BearerData.DISPLAY_MODE_USER);
+        assertEquals(bearerData.depositIndex, 1382);
+        assertEquals(bearerData.userResponseCode, 5);
+        assertEquals(bearerData.msgCenterTimeStamp.year, 2008);
+        assertEquals(bearerData.msgCenterTimeStamp.month, 11);
+        assertEquals(bearerData.msgCenterTimeStamp.monthDay, 1);
+        assertEquals(bearerData.msgCenterTimeStamp.hour, 11);
+        assertEquals(bearerData.msgCenterTimeStamp.minute, 1);
+        assertEquals(bearerData.msgCenterTimeStamp.second, 59);
+        assertEquals(bearerData.validityPeriodAbsolute, null);
+        assertEquals(bearerData.validityPeriodRelative, -63);
+        assertEquals(bearerData.deferredDeliveryTimeAbsolute.year, 1997);
+        assertEquals(bearerData.deferredDeliveryTimeAbsolute.month, 5);
+        assertEquals(bearerData.deferredDeliveryTimeAbsolute.monthDay, 18);
+        assertEquals(bearerData.deferredDeliveryTimeAbsolute.hour, 0);
+        assertEquals(bearerData.deferredDeliveryTimeAbsolute.minute, 0);
+        assertEquals(bearerData.deferredDeliveryTimeAbsolute.second, 0);
+        assertEquals(bearerData.deferredDeliveryTimeRelative, -57);
+        assertEquals(bearerData.hasUserDataHeader, false);
+        assertEquals(bearerData.userData.msgEncoding, UserData.ENCODING_7BIT_ASCII);
+        assertEquals(bearerData.userData.numFields, 2);
+        assertEquals(bearerData.userData.payloadStr, "hi");
+    }
+
+    @SmallTest
+    public void testMonolithicTwo() throws Exception {
+        String pdu = "0003200010010410168d200201050306081201110159050192060697061800000007013d0" +
+                "801c00901800a01e00b01030c01c00d01070e05039acc13880f018011020566";
+        BearerData bearerData = BearerData.decode(HexDump.hexStringToByteArray(pdu));
+        assertEquals(bearerData.messageType, BearerData.MESSAGE_TYPE_SUBMIT);
+        assertEquals(bearerData.messageId, 1);
+        assertEquals(bearerData.priority, BearerData.PRIORITY_EMERGENCY);
+        assertEquals(bearerData.privacy, BearerData.PRIVACY_CONFIDENTIAL);
+        assertEquals(bearerData.userAckReq, true);
+        assertEquals(bearerData.readAckReq, true);
+        assertEquals(bearerData.deliveryAckReq, true);
+        assertEquals(bearerData.reportReq, false);
+        assertEquals(bearerData.numberOfMessages, 3);
+        assertEquals(bearerData.alert, BearerData.ALERT_HIGH_PRIO);
+        assertEquals(bearerData.language, BearerData.LANGUAGE_HEBREW);
+        assertEquals(bearerData.callbackNumber.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF);
+        assertEquals(bearerData.callbackNumber.numberMode,
+                     CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK);
+        assertEquals(bearerData.callbackNumber.ton, CdmaSmsAddress.TON_UNKNOWN);
+        assertEquals(bearerData.callbackNumber.numberPlan, CdmaSmsAddress.NUMBERING_PLAN_UNKNOWN);
+        assertEquals(bearerData.callbackNumber.numberOfDigits, 7);
+        assertEquals(bearerData.callbackNumber.address, "3598271");
+        assertEquals(bearerData.displayMode, BearerData.DISPLAY_MODE_USER);
+        assertEquals(bearerData.depositIndex, 1382);
+        assertEquals(bearerData.userResponseCode, 5);
+        assertEquals(bearerData.msgCenterTimeStamp.year, 2008);
+        assertEquals(bearerData.msgCenterTimeStamp.month, 11);
+        assertEquals(bearerData.msgCenterTimeStamp.monthDay, 1);
+        assertEquals(bearerData.msgCenterTimeStamp.hour, 11);
+        assertEquals(bearerData.msgCenterTimeStamp.minute, 1);
+        assertEquals(bearerData.msgCenterTimeStamp.second, 59);
+        assertEquals(bearerData.validityPeriodAbsolute, null);
+        assertEquals(bearerData.validityPeriodRelative, 61);
+        assertEquals(bearerData.deferredDeliveryTimeAbsolute.year, 1997);
+        assertEquals(bearerData.deferredDeliveryTimeAbsolute.month, 5);
+        assertEquals(bearerData.deferredDeliveryTimeAbsolute.monthDay, 18);
+        assertEquals(bearerData.deferredDeliveryTimeAbsolute.hour, 0);
+        assertEquals(bearerData.deferredDeliveryTimeAbsolute.minute, 0);
+        assertEquals(bearerData.deferredDeliveryTimeAbsolute.second, 0);
+        assertEquals(bearerData.deferredDeliveryTimeRelative, -110);
+        assertEquals(bearerData.hasUserDataHeader, false);
+        assertEquals(bearerData.userData.msgEncoding, UserData.ENCODING_7BIT_ASCII);
+        assertEquals(bearerData.userData.numFields, 2);
+        assertEquals(bearerData.userData.payloadStr, "hi");
+    }
+
+    @SmallTest
     public void testUserDataHeaderConcatRefFeedback() throws Exception {
         BearerData bearerData = new BearerData();
         bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
@@ -410,22 +504,6 @@
     }
 
     @SmallTest
-    public void testMsgCenterTimeStampFeedback() throws Exception {
-        BearerData bearerData = new BearerData();
-        bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
-        bearerData.messageId = 0;
-        bearerData.hasUserDataHeader = false;
-        UserData userData = new UserData();
-        userData.payloadStr = "test message center timestamp";
-        bearerData.userData = userData;
-        bearerData.timeStamp = HexDump.hexStringToByteArray("112233445566");
-        byte []encodedSms = BearerData.encode(bearerData);
-        BearerData revBearerData = BearerData.decode(encodedSms);
-        assertEquals(HexDump.toHexString(bearerData.timeStamp),
-                     HexDump.toHexString(revBearerData.timeStamp));
-    }
-
-    @SmallTest
     public void testPrivacyIndicator() throws Exception {
         String pdu1 = "0003104090010c485f4194dfea34becf61b840090140";
         BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1));
diff --git a/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java b/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
index bdf67ba..6b56e6c 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
@@ -306,6 +307,14 @@
                 throws PackageManager.NameNotFoundException {
             return mRealContext.createPackageContext(packageName, flags);
         }
+
+        /**
+         * Message broadcast.  Pass through for now.
+         */
+        @Override
+        public void sendBroadcast(Intent intent) {
+            mRealContext.sendBroadcast(intent);
+        }
     }
 
 /**
@@ -361,7 +370,8 @@
         @Override 
         public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
             assertNotNull(intent);
-            assertEquals(intent.getAction(), Intent.ACTION_SEARCH);
+            assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH)
+                    || intent.getAction().equals(Intent.ACTION_WEB_SEARCH));
             switch (mSearchablesMode) {
             case SEARCHABLES_PASSTHROUGH:
                 return mRealPackageManager.queryIntentActivities(intent, flags);
@@ -375,7 +385,8 @@
         @Override
         public ResolveInfo resolveActivity(Intent intent, int flags) {
             assertNotNull(intent);
-            assertEquals(intent.getAction(), SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
+            assertTrue(intent.getAction().equals(Intent.ACTION_WEB_SEARCH)
+                    || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH));
             switch (mSearchablesMode) {
             case SEARCHABLES_PASSTHROUGH:
                 return mRealPackageManager.resolveActivity(intent, flags);
@@ -438,6 +449,29 @@
                 throw new UnsupportedOperationException();
             }
         }
+
+        /**
+         * Get the activity information for a particular activity.
+         *
+         * @param name The name of the activity to find.
+         * @param flags Additional option flags.
+         *
+         * @return ActivityInfo Information about the activity, if found, else null.
+         */
+        @Override
+        public ActivityInfo getActivityInfo(ComponentName name, int flags)
+                throws NameNotFoundException {
+            assertNotNull(name);
+            MoreAsserts.assertNotEqual(name, "");
+            switch (mSearchablesMode) {
+            case SEARCHABLES_PASSTHROUGH:
+                return mRealPackageManager.getActivityInfo(name, flags);
+            case SEARCHABLES_MOCK_ZERO:
+                throw new NameNotFoundException();
+            default:
+                throw new UnsupportedOperationException();
+            }
+        }
     }
 }
 
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProvider.java b/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProvider.java
new file mode 100644
index 0000000..b31ce18
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProvider.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.unit_tests.content;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.os.MemoryFile;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** Simple test provider that runs in the local process. */
+public class MemoryFileProvider extends ContentProvider {
+    private static final String TAG = "MemoryFileProvider";
+
+    private static final String DATA_FILE = "data.bin";
+
+    // some random data
+    public static final byte[] TEST_BLOB = new byte[] {
+        -12,  127, 0, 3, 1, 2, 3, 4, 5, 6, 1, -128, -1, -54, -65, 35,
+        -53, -96, -74, -74, -55, -43, -69, 3, 52, -58,
+        -121, 127, 87, -73, 16, -13, -103, -65, -128, -36,
+        107, 24, 118, -17, 97, 97, -88, 19, -94, -54,
+        53, 43, 44, -27, -124, 28, -74, 26, 35, -36,
+        16, -124, -31, -31, -128, -79, 108, 116, 43, -17 };
+
+    private SQLiteOpenHelper mOpenHelper;
+
+    private static final int DATA_ID_BLOB = 1;
+    private static final int HUGE = 2;
+    private static final int FILE = 3;
+
+    private static final UriMatcher sURLMatcher = new UriMatcher(
+            UriMatcher.NO_MATCH);
+
+    static {
+        sURLMatcher.addURI("*", "data/#/blob", DATA_ID_BLOB);
+        sURLMatcher.addURI("*", "huge", HUGE);
+        sURLMatcher.addURI("*", "file", FILE);
+    }
+
+    private static class DatabaseHelper extends SQLiteOpenHelper {
+        private static final String DATABASE_NAME = "local.db";
+        private static final int DATABASE_VERSION = 1;
+
+        public DatabaseHelper(Context context) {
+            super(context, DATABASE_NAME, null, DATABASE_VERSION);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE data (" +
+                       "_id INTEGER PRIMARY KEY," +
+                       "_blob TEXT, " +
+                       "integer INTEGER);");
+
+            // insert alarms
+            ContentValues values = new ContentValues();
+            values.put("_id", 1);
+            values.put("_blob", TEST_BLOB);
+            values.put("integer", 100);
+            db.insert("data", null, values);
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
+            Log.w(TAG, "Upgrading test database from version " +
+                  oldVersion + " to " + currentVersion +
+                  ", which will destroy all old data");
+            db.execSQL("DROP TABLE IF EXISTS data");
+            onCreate(db);
+        }
+    }
+
+
+    public MemoryFileProvider() {
+    }
+
+    @Override
+    public boolean onCreate() {
+        mOpenHelper = new DatabaseHelper(getContext());
+        try {
+            OutputStream out = getContext().openFileOutput(DATA_FILE, Context.MODE_PRIVATE);
+            out.write(TEST_BLOB);
+            out.close();
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri url, String[] projectionIn, String selection,
+            String[] selectionArgs, String sort) {
+        throw new UnsupportedOperationException("query not supported");
+    }
+
+    @Override
+    public String getType(Uri url) {
+        int match = sURLMatcher.match(url);
+        switch (match) {
+            case DATA_ID_BLOB:
+                return "application/octet-stream";
+            case FILE:
+                return "application/octet-stream";
+            default:
+                throw new IllegalArgumentException("Unknown URL");
+        }
+    }
+
+    @Override
+    public AssetFileDescriptor openAssetFile(Uri url, String mode) throws FileNotFoundException {
+        int match = sURLMatcher.match(url);
+        switch (match) {
+            case DATA_ID_BLOB:
+                String sql = "SELECT _blob FROM data WHERE _id=" + url.getPathSegments().get(1);
+                return getBlobColumnAsAssetFile(url, mode, sql);
+            case HUGE:
+                try {
+                    MemoryFile memoryFile = new MemoryFile(null, 5000000);
+                    memoryFile.writeBytes(TEST_BLOB, 0, 1000000, TEST_BLOB.length);
+                    memoryFile.deactivate();
+                    return AssetFileDescriptor.fromMemoryFile(memoryFile);
+                } catch (IOException ex) {
+                    throw new FileNotFoundException("Error reading " + url + ":" + ex.toString());
+                }
+            case FILE:
+                File file = getContext().getFileStreamPath(DATA_FILE);
+                ParcelFileDescriptor fd =
+                        ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+                return new AssetFileDescriptor(fd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
+            default:
+                throw new FileNotFoundException("No files supported by provider at " + url);
+        }
+    }
+
+    private AssetFileDescriptor getBlobColumnAsAssetFile(Uri url, String mode, String sql)
+            throws FileNotFoundException {
+        if (!"r".equals(mode)) {
+            throw new FileNotFoundException("Mode " + mode + " not supported for " + url);
+        }
+        try {
+            SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+            MemoryFile file = simpleQueryForBlobMemoryFile(db, sql);
+            if (file == null) throw new FileNotFoundException("No such entry: " + url);
+            AssetFileDescriptor afd = AssetFileDescriptor.fromMemoryFile(file);
+            file.deactivate();
+            // need to dup and then close? openFileHelper() doesn't do that though
+            return afd;
+        } catch (IOException ex) {
+            throw new FileNotFoundException("Error reading " + url + ":" + ex.toString());
+        }
+    }
+
+    private MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql) throws IOException {
+        Cursor cursor = db.rawQuery(sql, null);
+        try {
+            if (!cursor.moveToFirst()) {
+                return null;
+            }
+            byte[] bytes = cursor.getBlob(0);
+            MemoryFile file = new MemoryFile(null, bytes.length);
+            file.writeBytes(bytes, 0, 0, bytes.length);
+            return file;
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    @Override
+    public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+        throw new UnsupportedOperationException("update not supported");
+    }
+
+    @Override
+    public Uri insert(Uri url, ContentValues initialValues) {
+        throw new UnsupportedOperationException("insert not supported");
+    }
+
+    @Override
+    public int delete(Uri url, String where, String[] whereArgs) {
+        throw new UnsupportedOperationException("delete not supported");
+    }
+}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProviderTest.java b/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProviderTest.java
new file mode 100644
index 0000000..f88a9da
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProviderTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.unit_tests.content;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import java.io.InputStream;
+import java.util.Arrays;
+
+/**
+ * Tests reading a MemoryFile-based AssestFile from a ContentProvider running
+ * in a different process.
+ */
+public class MemoryFileProviderTest extends AndroidTestCase {
+
+    // reads from a cross-process AssetFileDescriptor for a MemoryFile
+    @MediumTest
+    public void testRead() throws Exception {
+        ContentResolver resolver = getContext().getContentResolver();
+        Uri uri = Uri.parse("content://com.android.unit_tests.content.MemoryFileProvider/data/1/blob");
+        byte[] buf = new byte[MemoryFileProvider.TEST_BLOB.length];
+        InputStream in = resolver.openInputStream(uri);
+        assertNotNull(in);
+        int count = in.read(buf);
+        assertEquals(buf.length, count);
+        assertEquals(-1, in.read());
+        in.close();
+        assertTrue(Arrays.equals(MemoryFileProvider.TEST_BLOB, buf));
+    }
+
+    // tests that we don't leak file descriptors or virtual address space
+    @MediumTest
+    public void testClose() throws Exception {
+        ContentResolver resolver = getContext().getContentResolver();
+        // open enough file descriptors that we will crash something if we leak FDs
+        // or address space
+        for (int i = 0; i < 1025; i++) {
+            Uri uri = Uri.parse("content://com.android.unit_tests.content.MemoryFileProvider/huge");
+            InputStream in = resolver.openInputStream(uri);
+            assertNotNull("Failed to open stream number " + i, in);
+            assertEquals(1000000, in.skip(1000000));
+            byte[] buf = new byte[MemoryFileProvider.TEST_BLOB.length];
+            int count = in.read(buf);
+            assertEquals(buf.length, count);
+            assertTrue(Arrays.equals(MemoryFileProvider.TEST_BLOB, buf));
+            in.close();
+        }
+    }
+
+    // tests that we haven't broken AssestFileDescriptors for normal files.
+    @MediumTest
+    public void testFile() throws Exception {
+        ContentResolver resolver = getContext().getContentResolver();
+        Uri uri = Uri.parse("content://com.android.unit_tests.content.MemoryFileProvider/file");
+        byte[] buf = new byte[MemoryFileProvider.TEST_BLOB.length];
+        InputStream in = resolver.openInputStream(uri);
+        assertNotNull(in);
+        int count = in.read(buf);
+        assertEquals(buf.length, count);
+        assertEquals(-1, in.read());
+        in.close();
+        assertTrue(Arrays.equals(MemoryFileProvider.TEST_BLOB, buf));
+    }
+
+}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java b/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java
index 5161f7b..18b3d63 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java
@@ -17,19 +17,21 @@
 package com.android.unit_tests.os;
 
 import android.os.MemoryFile;
+import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
 
-import junit.framework.TestCase;
-
-public class MemoryFileTest extends TestCase {
+public class MemoryFileTest extends AndroidTestCase {
 
     private void compareBuffers(byte[] buffer1, byte[] buffer2, int length) throws Exception {
         for (int i = 0; i < length; i++) {
@@ -95,6 +97,74 @@
         file.close();
     }
 
+    // Tests for the IndexOutOfBoundsException cases in read().
+
+    private void readIndexOutOfBoundsException(int offset, int count, String msg)
+            throws Exception {
+        MemoryFile file = new MemoryFile("MemoryFileTest", testString.length);
+        try {
+            file.writeBytes(testString, 0, 0, testString.length);
+            InputStream is = file.getInputStream();
+            byte[] buffer = new byte[testString.length + 10];
+            try {
+                is.read(buffer, offset, count);
+                fail(msg);
+            } catch (IndexOutOfBoundsException ex) {
+                // this is what should happen
+            } finally {
+                is.close();
+            }
+        } finally {
+            file.close();
+        }
+    }
+
+    @SmallTest
+    public void testReadNegativeOffset() throws Exception {
+        readIndexOutOfBoundsException(-1, 5,
+                "read() with negative offset should throw IndexOutOfBoundsException");
+    }
+
+    @SmallTest
+    public void testReadNegativeCount() throws Exception {
+        readIndexOutOfBoundsException(5, -1,
+                "read() with negative length should throw IndexOutOfBoundsException");
+    }
+
+    @SmallTest
+    public void testReadOffsetOverflow() throws Exception {
+        readIndexOutOfBoundsException(testString.length + 10, 5,
+                "read() with offset outside buffer should throw IndexOutOfBoundsException");
+    }
+
+    @SmallTest
+    public void testReadOffsetCountOverflow() throws Exception {
+        readIndexOutOfBoundsException(testString.length, 11,
+                "read() with offset + count outside buffer should throw IndexOutOfBoundsException");
+    }
+
+    // Test behavior of read() at end of file
+    @SmallTest
+    public void testReadEOF() throws Exception {
+        MemoryFile file = new MemoryFile("MemoryFileTest", testString.length);
+        try {
+            file.writeBytes(testString, 0, 0, testString.length);
+            InputStream is = file.getInputStream();
+            try {
+                byte[] buffer = new byte[testString.length + 10];
+                // read() with count larger than data should succeed, and return # of bytes read
+                assertEquals(testString.length, is.read(buffer));
+                compareBuffers(testString, buffer, testString.length);
+                // Read at EOF should return -1
+                assertEquals(-1, is.read());
+            } finally {
+                is.close();
+            }
+        } finally {
+            file.close();
+        }
+    }
+
     // Tests that close() is idempotent
     @SmallTest
     public void testCloseClose() throws Exception {
@@ -163,6 +233,51 @@
         }
     }
 
+    @SmallTest
+    public void testIsMemoryFile() throws Exception {
+        MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+        FileDescriptor fd = file.getFileDescriptor();
+        assertNotNull(fd);
+        assertTrue(fd.valid());
+        assertTrue(MemoryFile.isMemoryFile(fd));
+        file.close();
+
+        assertFalse(MemoryFile.isMemoryFile(FileDescriptor.in));
+        assertFalse(MemoryFile.isMemoryFile(FileDescriptor.out));
+        assertFalse(MemoryFile.isMemoryFile(FileDescriptor.err));
+
+        File tempFile = File.createTempFile("MemoryFileTest",".tmp", getContext().getFilesDir());
+        assertNotNull(file);
+        FileOutputStream out = null;
+        try {
+            out = new FileOutputStream(tempFile);
+            FileDescriptor fileFd = out.getFD();
+            assertNotNull(fileFd);
+            assertFalse(MemoryFile.isMemoryFile(fileFd));
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+            tempFile.delete();
+        }
+    }
+
+    @SmallTest
+    public void testFileDescriptor() throws Exception {
+        MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+        MemoryFile ref = new MemoryFile(file.getFileDescriptor(), file.length(), "r");
+        byte[] buffer;
+
+        // write to original, read from reference
+        file.writeBytes(testString, 0, 2000, testString.length);
+        buffer = new byte[testString.length];
+        ref.readBytes(buffer, 2000, 0, testString.length);
+        compareBuffers(testString, buffer, testString.length);
+
+        file.close();
+        ref.close();  // Doesn't actually do anything, since the file descriptor is not dup(2):ed
+    }
+
     private static final byte[] testString = new byte[] {
         3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, 2, 7, 9, 5, 0, 2, 8, 8, 4, 1, 9, 7, 1, 6, 9, 3, 9, 9, 3, 7, 5, 1, 0, 5, 8, 2, 0, 9, 7, 4, 9, 4, 4, 5, 9, 2, 3, 0, 7, 8, 1, 6, 4,
         0, 6, 2, 8, 6, 2, 0, 8, 9, 9, 8, 6, 2, 8, 0, 3, 4, 8, 2, 5, 3, 4, 2, 1, 1, 7, 0, 6, 7, 9, 8, 2, 1, 4, 8, 0, 8, 6, 5, 1, 3, 2, 8, 2, 3, 0, 6, 6, 4, 7, 0, 9, 3, 8, 4, 4, 6, 0, 9, 5, 5, 0, 5, 8, 2, 2, 3, 1, 7, 2,
diff --git a/tests/DumpRenderTree/assets/run_reliability_tests.py b/tests/DumpRenderTree/assets/run_reliability_tests.py
index c12c783..076c508 100755
--- a/tests/DumpRenderTree/assets/run_reliability_tests.py
+++ b/tests/DumpRenderTree/assets/run_reliability_tests.py
@@ -10,8 +10,10 @@
 
 import logging
 import optparse
+import os
 import subprocess
 import sys
+import time
 
 TEST_LIST_FILE = "/sdcard/android/reliability_tests_list.txt"
 TEST_STATUS_FILE = "/sdcard/android/reliability_running_test.txt"
@@ -46,6 +48,20 @@
                    stderr=subprocess.PIPE).communicate()
 
 
+def Bugreport(url, bugreport_dir, adb_cmd):
+  """Pull a bugreport from the device."""
+  bugreport_filename = "%s/reliability_bugreport_%d.txt" % (bugreport_dir,
+                                                            int(time.time()))
+
+  # prepend the report with url
+  handle = open(bugreport_filename, "w")
+  handle.writelines("Bugreport for crash in url - %s\n\n" % url)
+  handle.close()
+
+  cmd = "%s bugreport >> %s" % (adb_cmd, bugreport_filename)
+  os.system(cmd)
+
+
 def main(options, args):
   """Send the url list to device and start testing, restart if crashed."""
 
@@ -75,6 +91,21 @@
   else:
     timedout_file = options.timeout_file
 
+  if not options.delay:
+    manual_delay = 0
+  else:
+    manual_delay = options.delay
+
+  if not options.bugreport:
+    bugreport_dir = "."
+  else:
+    bugreport_dir = options.bugreport
+  if not os.path.exists(bugreport_dir):
+    os.makedirs(bugreport_dir)
+  if not os.path.isdir(bugreport_dir):
+    logging.error("Cannot create results dir: " + bugreport_dir)
+    sys.exit(1)
+
   adb_cmd = "adb "
   if options.adb_options:
     adb_cmd += options.adb_options + " "
@@ -110,8 +141,8 @@
   # Call ReliabilityTestsAutoTest#startReliabilityTests
   test_cmd = (test_cmd_prefix + " -e class "
               "com.android.dumprendertree.ReliabilityTest#"
-              "runTest -e timeout %d %s" %
-              (timeout_ms, test_cmd_postfix))
+              "runReliabilityTest -e timeout %s -e delay %s %s" %
+              (str(timeout_ms), str(manual_delay), test_cmd_postfix))
 
   adb_output = subprocess.Popen(test_cmd, shell=True,
                                 stdout=subprocess.PIPE,
@@ -123,12 +154,9 @@
                                     stdout=subprocess.PIPE).communicate()[0]
     logging.info(crashed_test + " CRASHED")
     crashed_tests.append(crashed_test)
+    Bugreport(crashed_test, bugreport_dir, adb_cmd)
     logging.info("Resuming reliability test runner...")
 
-    test_cmd = (test_cmd_prefix + " -e class "
-                "com.android.dumprendertree.ReliabilityTest#"
-                "runTest -e timeout %d %s" %
-                (timeout_ms, test_cmd_postfix))
     adb_output = subprocess.Popen(test_cmd, shell=True, stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE).communicate()[0]
 
@@ -157,20 +185,26 @@
 
 if "__main__" == __name__:
   option_parser = optparse.OptionParser()
-  option_parser.add_option("", "--time-out-ms",
+  option_parser.add_option("-t", "--time-out-ms",
                            default=60000,
                            help="set the timeout for each test")
-  option_parser.add_option("", "--verbose", action="store_true",
+  option_parser.add_option("-v", "--verbose", action="store_true",
                            default=False,
                            help="include debug-level logging")
-  option_parser.add_option("", "--adb-options",
+  option_parser.add_option("-a", "--adb-options",
                            default=None,
                            help="pass options to adb, such as -d -e, etc")
-  option_parser.add_option("", "--crash-file",
+  option_parser.add_option("-c", "--crash-file",
                            default="reliability_crashed_sites.txt",
                            help="the list of sites that cause browser to crash")
-  option_parser.add_option("", "--timeout-file",
+  option_parser.add_option("-f", "--timeout-file",
                            default="reliability_timedout_sites.txt",
-                           help="the list of sites that timedout during test.")
+                           help="the list of sites that timedout during test")
+  option_parser.add_option("-d", "--delay",
+                           default=0,
+                           help="add a manual delay between pages (in ms)")
+  option_parser.add_option("-b", "--bugreport",
+                           default=".",
+                           help="the directory to store bugreport for crashes")
   opts, arguments = option_parser.parse_args()
   main(opts, arguments)
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java
index ebdc9c7..57e06a1 100755
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java
@@ -16,14 +16,11 @@
 
 package com.android.dumprendertree;
 
-import junit.framework.TestSuite;
-import com.android.dumprendertree.LayoutTestsAutoTest;
-
+import android.os.Bundle;
 import android.test.InstrumentationTestRunner;
 import android.test.InstrumentationTestSuite;
-import android.util.Log;
-import android.content.Intent;
-import android.os.Bundle;
+
+import junit.framework.TestSuite;
 
 
 /**
@@ -61,6 +58,14 @@
             }
         }
         
+        String delay_str = (String) icicle.get("delay");
+        if(delay_str != null) {
+            try {
+                this.mDelay = Integer.parseInt(delay_str);
+            } catch (Exception e) {
+            }
+        }
+        
         String r = (String)icicle.get("rebaseline");
         this.mRebaseline = (r != null && r.toLowerCase().equals("true"));
         super.onCreate(icicle);
@@ -68,6 +73,7 @@
     
     public String mTestPath = null;
     public int mTimeoutInMillis = 0;
+    public int mDelay = 0;
     public boolean mRebaseline = false;
 }
 
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
index 8f0ca9f..22c458c 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
@@ -25,13 +25,12 @@
     static final String RELIABILITY_TEST_RUNNER_FILES[] = {
         "run_reliability_tests.py"
     };
-
+    
     public ReliabilityTest() {
         super(PKG_NAME, ReliabilityTestActivity.class);
     }
     
-    @Override
-    protected void runTest() throws Throwable {
+    public void runReliabilityTest() throws Throwable {
         ReliabilityTestActivity activity = getActivity();
         LayoutTestsAutoRunner runner = (LayoutTestsAutoRunner)getInstrumentation();
         
@@ -52,9 +51,13 @@
         Handler handler = null;
         boolean timeoutFlag = false;
         long start, elapsed;
+        
         //read from BufferedReader instead of populating a list in advance,
         //this will avoid excessive memory usage in case of a large list
         while((url = listReader.readLine()) != null) {
+            url = url.trim();
+            if(url.length() == 0)
+                continue;
             start = System.currentTimeMillis();
             Log.v(LOGTAG, "Testing URL: " + url);
             updateTestStatus(url);
@@ -64,7 +67,7 @@
             handler = activity.getHandler();
             handler.sendMessage(handler.obtainMessage(
                     ReliabilityTestActivity.MSG_NAVIGATE, 
-                    runner.mTimeoutInMillis, 0, url));
+                    runner.mTimeoutInMillis, runner.mDelay, url));
             timeoutFlag = activity.waitUntilDone();
             elapsed = System.currentTimeMillis() - start;
             if(elapsed < 1000) {
@@ -80,6 +83,7 @@
             System.gc();
             System.gc();
         }
+        updateTestStatus(TEST_DONE);
         activity.finish();
         listReader.close();
     }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java
index 75f1400..cbec104 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java
@@ -39,6 +39,8 @@
     private boolean pageDone;
     private Object pageDoneLock;
     private int pageStartCount;
+    private int manualDelay;
+    private PageDoneRunner pageDoneRunner = new PageDoneRunner();
     
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -73,6 +75,7 @@
                         handleTimeout();
                         return;
                     case MSG_NAVIGATE:
+                        manualDelay = msg.arg2;
                         navigate((String)msg.obj, msg.arg1);
                         return;
                 }
@@ -212,6 +215,12 @@
         }
         
         @Override
+        public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
+            result.confirm();
+            return true;
+        }
+
+        @Override
         public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
                 JsPromptResult result) {
             result.confirm();
@@ -246,11 +255,18 @@
         public void run() {
             if (initialStartCount == pageStartCount) {
                 //perform cleanup
-                webView.stopLoading();
-                Log.v(LOGTAG, "Finishing URL: " + webView.getUrl());
                 handler.removeMessages(MSG_TIMEOUT);
-                setPageDone(true);
+                webView.stopLoading();
+                handler.postDelayed(pageDoneRunner, manualDelay);
             }
         }
     }
+    
+    class PageDoneRunner implements Runnable {
+        
+        public void run() {
+            Log.v(LOGTAG, "Finishing URL: " + webView.getUrl());
+            setPageDone(true);
+        }
+    }
 }
diff --git a/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java b/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java
index 27241b8..e9a43d7 100644
--- a/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java
+++ b/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java
@@ -92,10 +92,6 @@
     }
 
     public void testInsertWithBackRefs() throws OperationApplicationException {
-        ContentValues valuesBackRefs = new ContentValues();
-        valuesBackRefs.put("a1", 3);
-        valuesBackRefs.put("a2", 1);
-
         ContentProviderResult[] previousResults = new ContentProviderResult[4];
         previousResults[0] = new ContentProviderResult(100);
         previousResults[1] = new ContentProviderResult(101);
@@ -103,7 +99,8 @@
         previousResults[3] = new ContentProviderResult(103);
         ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
                 .withValues(sTestValues1)
-                .withValueBackReferences(valuesBackRefs)
+                .withValueBackReference("a1", 3)
+                .withValueBackReference("a2", 1)
                 .build();
         ContentProviderResult result = op1.apply(new TestContentProvider() {
             public Uri insert(Uri uri, ContentValues values) {
@@ -146,11 +143,6 @@
         previousResults[2] = new ContentProviderResult(102);
         previousResults[3] = new ContentProviderResult(103);
 
-        ContentValues valuesBackRefs = new ContentValues();
-        valuesBackRefs.put("a1", 3); // a1 -> 103
-        valuesBackRefs.put("a2", 1); // a2 -> 101
-        valuesBackRefs.put("a3", 2); // a3 -> 102
-
         ContentValues expectedValues = new ContentValues(values);
         expectedValues.put("a1", "103");
         expectedValues.put("a2", "101");
@@ -158,18 +150,15 @@
 
         ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
                 .withValues(values)
-                .withValueBackReferences(valuesBackRefs)
+                .withValueBackReference("a1", 3)
+                .withValueBackReference("a2", 1)
+                .withValueBackReference("a3", 2)
                 .build();
         ContentValues v2 = op1.resolveValueBackReferences(previousResults, previousResults.length);
         assertEquals(expectedValues, v2);
     }
 
     public void testSelectionBackRefs() {
-        Map<Integer, Integer> selectionBackRefs = new Hashtable<Integer, Integer>();
-        selectionBackRefs.put(1, 3);
-        selectionBackRefs.put(2, 1);
-        selectionBackRefs.put(4, 2);
-
         ContentProviderResult[] previousResults = new ContentProviderResult[4];
         previousResults[0] = new ContentProviderResult(100);
         previousResults[1] = new ContentProviderResult(101);
@@ -179,7 +168,9 @@
         String[] selectionArgs = new String[]{"a", null, null, "b", null};
 
         ContentProviderOperation op1 = ContentProviderOperation.newUpdate(sTestUri1)
-                .withSelectionBackReferences(selectionBackRefs)
+                .withSelectionBackReference(1, 3)
+                .withSelectionBackReference(2, 1)
+                .withSelectionBackReference(4, 2)
                 .withSelection("unused", selectionArgs)
                 .build();
         String[] s2 = op1.resolveSelectionArgsBackReferences(
@@ -209,7 +200,6 @@
             ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(
                     Uri.parse("content://goo/bar"));
 
-            builderSetEntity(builder, new TestEntity("blah"));
             builderSetExpectedCount(builder, 42);
             builderSetSelection(builder, "selection");
             builderSetSelectionArgs(builder, new String[]{"a", "b"});
@@ -224,7 +214,6 @@
 
             assertEquals(1 /* ContentProviderOperation.TYPE_INSERT */, operationGetType(op2));
             assertEquals("content://goo/bar", operationGetUri(op2).toString());
-            assertEquals("blah", ((TestEntity) operationGetEntity(op2)).mValue);
             assertEquals(Integer.valueOf(42), operationGetExpectedCount(op2));
             assertEquals("selection", operationGetSelection(op2));
             assertEquals(2, operationGetSelectionArgs(op2).length);
@@ -422,35 +411,6 @@
         return (Map<Integer, Integer>) field.get(operation);
     }
 
-    public static class TestEntity extends Entity {
-        public final String mValue;
-        public TestEntity(String value) {
-            mValue = value;
-        }
-
-        public TestEntity(Parcel source) {
-            mValue = source.readString();
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeString(mValue);
-        }
-
-        public static final Creator<TestEntity> CREATOR = new Creator<TestEntity>() {
-            public TestEntity createFromParcel(Parcel source) {
-                return new TestEntity(source);
-            }
-
-            public TestEntity[] newArray(int size) {
-                return new TestEntity[size];
-            }
-        };
-    }
-
     public void testParcelingResult() {
         Parcel parcel = Parcel.obtain();
         ContentProviderResult result1;
diff --git a/tests/backup/backup_helper_test.cpp b/tests/backup/backup_helper_test.cpp
index 1085909..f087941 100644
--- a/tests/backup/backup_helper_test.cpp
+++ b/tests/backup/backup_helper_test.cpp
@@ -1,4 +1,20 @@
-#include <utils/backup_helpers.h>
+/*
+ * 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 <utils/BackupHelpers.h>
 
 #include <stdio.h>
 #include <string.h>
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 2ef0347..3f37069 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -15,7 +15,7 @@
 #include <utils/String8.h>
 #include <utils/Vector.h>
 #include <utils/RefBase.h>
-#include <utils/ZipFile.h>
+#include "ZipFile.h"
 
 #include "Bundle.h"
 #include "SourcePos.h"
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index fdc859c..2d8973d 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -17,7 +17,10 @@
 	ResourceTable.cpp \
 	Images.cpp \
 	Resource.cpp \
-    SourcePos.cpp
+    SourcePos.cpp \
+    ZipEntry.cpp \
+    ZipFile.cpp
+
 
 LOCAL_CFLAGS += -Wno-format-y2k
 
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 32df631..0e889f5 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -12,7 +12,6 @@
 #include <utils/threads.h>
 #include <utils/List.h>
 #include <utils/Errors.h>
-#include <utils/ZipFile.h>
 
 #include <fcntl.h>
 #include <errno.h>
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 6ff8269..a33b4d7 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -10,7 +10,6 @@
 #include <utils/threads.h>
 #include <utils/List.h>
 #include <utils/Errors.h>
-#include <utils/ZipFile.h>
 
 #include <stdlib.h>
 #include <getopt.h>
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index b5c334c..34ca5e5 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -12,7 +12,7 @@
 #include <utils/Errors.h>
 #include "Bundle.h"
 #include "AaptAssets.h"
-#include <utils/ZipFile.h>
+#include "ZipFile.h"
 
 extern int doVersion(Bundle* bundle);
 extern int doList(Bundle* bundle);
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 8900529..8424169 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -11,7 +11,6 @@
 #include <utils/threads.h>
 #include <utils/List.h>
 #include <utils/Errors.h>
-#include <utils/ZipFile.h>
 
 #include <sys/types.h>
 #include <dirent.h>
diff --git a/libs/utils/ZipEntry.cpp b/tools/aapt/ZipEntry.cpp
similarity index 99%
rename from libs/utils/ZipEntry.cpp
rename to tools/aapt/ZipEntry.cpp
index fbc9e67..bed0333 100644
--- a/libs/utils/ZipEntry.cpp
+++ b/tools/aapt/ZipEntry.cpp
@@ -20,8 +20,8 @@
 
 #define LOG_TAG "zip"
 
-#include "utils/ZipEntry.h"
-#include "utils/Log.h"
+#include "ZipEntry.h"
+#include <utils/Log.h>
 
 #include <stdio.h>
 #include <string.h>
diff --git a/include/utils/ZipEntry.h b/tools/aapt/ZipEntry.h
similarity index 99%
rename from include/utils/ZipEntry.h
rename to tools/aapt/ZipEntry.h
index e4698df..7f721b4 100644
--- a/include/utils/ZipEntry.h
+++ b/tools/aapt/ZipEntry.h
@@ -22,7 +22,7 @@
 #ifndef __LIBS_ZIPENTRY_H
 #define __LIBS_ZIPENTRY_H
 
-#include "Errors.h"
+#include <utils/Errors.h>
 
 #include <stdlib.h>
 #include <stdio.h>
diff --git a/libs/utils/ZipFile.cpp b/tools/aapt/ZipFile.cpp
similarity index 99%
rename from libs/utils/ZipFile.cpp
rename to tools/aapt/ZipFile.cpp
index 2132d22..62c9383 100644
--- a/libs/utils/ZipFile.cpp
+++ b/tools/aapt/ZipFile.cpp
@@ -20,9 +20,10 @@
 
 #define LOG_TAG "zip"
 
-#include "utils/ZipFile.h"
-#include "utils/ZipUtils.h"
-#include "utils/Log.h"
+#include <utils/ZipUtils.h>
+#include <utils/Log.h>
+
+#include "ZipFile.h"
 
 #include <zlib.h>
 #define DEF_MEM_LEVEL 8                // normally in zutil.h?
diff --git a/include/utils/ZipFile.h b/tools/aapt/ZipFile.h
similarity index 98%
rename from include/utils/ZipFile.h
rename to tools/aapt/ZipFile.h
index 44df5bb..dbbd072 100644
--- a/include/utils/ZipFile.h
+++ b/tools/aapt/ZipFile.h
@@ -21,11 +21,12 @@
 #ifndef __LIBS_ZIPFILE_H
 #define __LIBS_ZIPFILE_H
 
-#include "ZipEntry.h"
-#include "Vector.h"
-#include "Errors.h"
+#include <utils/Vector.h>
+#include <utils/Errors.h>
 #include <stdio.h>
 
+#include "ZipEntry.h"
+
 namespace android {
 
 /*
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
index f434e14..baa3d53 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -1145,12 +1145,4 @@
     public Context getApplicationContext() {
         throw new UnsupportedOperationException();
     }
-
-    /**
-     * @hide
-     */
-    @Override
-    public float getApplicationScale() {
-        throw new UnsupportedOperationException();
-    }
 }
diff --git a/tools/localize/Android.mk b/tools/localize/Android.mk
index 186177f5..ab79f8d 100644
--- a/tools/localize/Android.mk
+++ b/tools/localize/Android.mk
@@ -53,4 +53,3 @@
 endif
 
 include $(BUILD_HOST_EXECUTABLE)
-
diff --git a/tools/localize/file_utils.cpp b/tools/localize/file_utils.cpp
index c0981d8..84081f5 100644
--- a/tools/localize/file_utils.cpp
+++ b/tools/localize/file_utils.cpp
@@ -3,15 +3,49 @@
 #include <unistd.h>
 #include "file_utils.h"
 #include "Perforce.h"
+#include <utils/String8.h>
 #include <sys/fcntl.h>
 #include <sys/stat.h>
 #include <errno.h>
 #include <string.h>
 #include <stdlib.h>
 #include <cstdio>
-#include <host/Directories.h>
 #include "log.h"
 
+using namespace android;
+using namespace std;
+
+static string
+parent_dir(const string& path)
+{
+    return string(String8(path.c_str()).getPathDir().string());
+}
+
+static int
+mkdirs(const char* last)
+{
+    String8 dest;
+    const char* s = last-1;
+    int err;
+    do {
+        s++;
+        if (s > last && (*s == '.' || *s == 0)) {
+            String8 part(last, s-last);
+            dest.appendPath(part);
+#ifdef HAVE_MS_C_RUNTIME
+            err = _mkdir(dest.string());
+#else                    
+            err = mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
+#endif                    
+            if (err != 0) {
+                return err;
+            }
+            last = s+1;
+        }
+    } while (*s);
+    return 0;
+}
+
 string
 translated_file_name(const string& file, const string& locale)
 {
diff --git a/tts/java/android/tts/SynthProxy.java b/tts/java/android/tts/SynthProxy.java
new file mode 100755
index 0000000..e065f40
--- /dev/null
+++ b/tts/java/android/tts/SynthProxy.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.tts;
+
+import android.util.Log;
+import java.lang.ref.WeakReference;
+
+/**
+ * @hide
+ *
+ * The SpeechSynthesis class provides a high-level api to create and play
+ * synthesized speech. This class is used internally to talk to a native
+ * TTS library that implements the interface defined in
+ * frameworks/base/include/tts/TtsEngine.h
+ *
+ */
+@SuppressWarnings("unused")
+public class SynthProxy {
+
+    //
+    // External API
+    //
+
+    /**
+     * Constructor; pass the location of the native TTS .so to use.
+     */
+    public SynthProxy(String nativeSoLib) {
+        Log.e("TTS is loading", nativeSoLib);
+        native_setup(new WeakReference<SynthProxy>(this), nativeSoLib);
+    }
+
+    /**
+     * Stops and clears the AudioTrack.
+     */
+    public void stop() {
+        native_stop(mJniData);
+    }
+
+    /**
+     * Synthesize speech and speak it directly using AudioTrack.
+     */
+    public void speak(String text) {
+        native_speak(mJniData, text);
+    }
+
+    /**
+     * Synthesize speech to a file. The current implementation writes a valid
+     * WAV file to the given path, assuming it is writable. Something like
+     * "/sdcard/???.wav" is recommended.
+     */
+    public void synthesizeToFile(String text, String filename) {
+        native_synthesizeToFile(mJniData, text, filename);
+    }
+
+    // TODO add IPA methods
+
+    /**
+     * Sets the language
+     */
+    public void setLanguage(String language) {
+        native_setLanguage(mJniData, language);
+    }
+
+    /**
+     * Sets the speech rate
+     */
+    public final void setSpeechRate(int speechRate) {
+        native_setSpeechRate(mJniData, speechRate);
+    }
+
+
+    /**
+     * Plays the given audio buffer
+     */
+    public void playAudioBuffer(int bufferPointer, int bufferSize) {
+        native_playAudioBuffer(mJniData, bufferPointer, bufferSize);
+    }
+
+    /**
+     * Gets the currently set language
+     */
+    public String getLanguage() {
+        return native_getLanguage(mJniData);
+    }
+
+    /**
+     * Gets the currently set rate
+     */
+    public int getRate() {
+        return native_getRate(mJniData);
+    }
+
+    /**
+     * Shuts down the native synthesizer
+     */
+    public void shutdown()  {
+        native_shutdown(mJniData);
+    }
+
+    //
+    // Internal
+    //
+
+    protected void finalize() {
+        native_finalize(mJniData);
+        mJniData = 0;
+    }
+
+    static {
+        System.loadLibrary("ttssynthproxy");
+    }
+
+    private final static String TAG = "SynthProxy";
+
+    /**
+     * Accessed by native methods
+     */
+    private int mJniData = 0;
+
+    private native final void native_setup(Object weak_this,
+            String nativeSoLib);
+
+    private native final void native_finalize(int jniData);
+
+    private native final void native_stop(int jniData);
+
+    private native final void native_speak(int jniData, String text);
+
+    private native final void native_synthesizeToFile(int jniData, String text, String filename);
+
+    private native final void native_setLanguage(int jniData, String language);
+
+    private native final void native_setSpeechRate(int jniData, int speechRate);
+
+    // TODO add buffer format
+    private native final void native_playAudioBuffer(int jniData, int bufferPointer, int bufferSize);
+
+    private native final String native_getLanguage(int jniData);
+
+    private native final int native_getRate(int jniData);
+
+    private native final void native_shutdown(int jniData);
+
+
+    /**
+     * Callback from the C layer
+     */
+    @SuppressWarnings("unused")
+    private static void postNativeSpeechSynthesizedInJava(Object tts_ref,
+            int bufferPointer, int bufferSize) {
+
+        Log.i("TTS plugin debug", "bufferPointer: " + bufferPointer
+                + " bufferSize: " + bufferSize);
+
+        SynthProxy nativeTTS = (SynthProxy)((WeakReference)tts_ref).get();
+        // TODO notify TTS service of synthesis/playback completion,
+        //      method definition to be changed.
+    }
+}
diff --git a/tts/java/android/tts/TtsService.java b/tts/java/android/tts/TtsService.java
new file mode 100755
index 0000000..d3171818
--- /dev/null
+++ b/tts/java/android/tts/TtsService.java
@@ -0,0 +1,783 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.tts;
+
+import android.tts.ITts.Stub;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * @hide Synthesizes speech from text. This is implemented as a service so that
+ *       other applications can call the TTS without needing to bundle the TTS
+ *       in the build.
+ *
+ */
+public class TtsService extends Service implements OnCompletionListener {
+
+    private static class SpeechItem {
+        public static final int SPEECH = 0;
+        public static final int EARCON = 1;
+        public static final int SILENCE = 2;
+        public String mText = null;
+        public ArrayList<String> mParams = null;
+        public int mType = SPEECH;
+        public long mDuration = 0;
+
+        public SpeechItem(String text, ArrayList<String> params, int itemType) {
+            mText = text;
+            mParams = params;
+            mType = itemType;
+        }
+
+        public SpeechItem(long silenceTime) {
+            mDuration = silenceTime;
+        }
+    }
+
+    /**
+     * Contains the information needed to access a sound resource; the name of
+     * the package that contains the resource and the resID of the resource
+     * within that package.
+     */
+    private static class SoundResource {
+        public String mSourcePackageName = null;
+        public int mResId = -1;
+        public String mFilename = null;
+
+        public SoundResource(String packageName, int id) {
+            mSourcePackageName = packageName;
+            mResId = id;
+            mFilename = null;
+        }
+
+        public SoundResource(String file) {
+            mSourcePackageName = null;
+            mResId = -1;
+            mFilename = file;
+        }
+    }
+
+    private static final String ACTION = "android.intent.action.USE_TTS";
+    private static final String CATEGORY = "android.intent.category.TTS";
+    private static final String PKGNAME = "android.tts";
+
+    final RemoteCallbackList<ITtsCallback> mCallbacks = new RemoteCallbackList<ITtsCallback>();
+
+    private Boolean mIsSpeaking;
+    private ArrayList<SpeechItem> mSpeechQueue;
+    private HashMap<String, SoundResource> mEarcons;
+    private HashMap<String, SoundResource> mUtterances;
+    private MediaPlayer mPlayer;
+    private TtsService mSelf;
+
+    private SharedPreferences prefs;
+
+    private final ReentrantLock speechQueueLock = new ReentrantLock();
+    private final ReentrantLock synthesizerLock = new ReentrantLock();
+
+    // TODO support multiple SpeechSynthesis objects
+    private SynthProxy nativeSynth;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i("TTS", "TTS starting");
+
+
+        // TODO: Make this work when the settings are done in the main Settings
+        // app.
+        prefs = PreferenceManager.getDefaultSharedPreferences(this);
+
+        // TODO: This should be changed to work by requesting the path
+        // from the default engine.
+        nativeSynth = new SynthProxy(prefs.getString("engine_pref", ""));
+
+
+        mSelf = this;
+        mIsSpeaking = false;
+
+        mEarcons = new HashMap<String, SoundResource>();
+        mUtterances = new HashMap<String, SoundResource>();
+
+        mSpeechQueue = new ArrayList<SpeechItem>();
+        mPlayer = null;
+
+        setLanguage(prefs.getString("lang_pref", "en-rUS"));
+        setSpeechRate(Integer.parseInt(prefs.getString("rate_pref", "140")));
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        // Don't hog the media player
+        cleanUpPlayer();
+
+        nativeSynth.shutdown();
+
+        // Unregister all callbacks.
+        mCallbacks.kill();
+    }
+
+    private void setSpeechRate(int rate) {
+        if (prefs.getBoolean("override_pref", false)) {
+            // This is set to the default here so that the preview in the prefs
+            // activity will show the change without a restart, even if apps are
+            // not allowed to change the defaults.
+            rate = Integer.parseInt(prefs.getString("rate_pref", "140"));
+        }
+        nativeSynth.setSpeechRate(rate);
+    }
+
+    private void setLanguage(String lang) {
+        if (prefs.getBoolean("override_pref", false)) {
+            // This is set to the default here so that the preview in the prefs
+            // activity will show the change without a restart, even if apps are
+            // not
+            // allowed to change the defaults.
+            lang = prefs.getString("lang_pref", "en-rUS");
+        }
+        nativeSynth.setLanguage(lang);
+    }
+
+    private void setEngine(String engineName, String[] requestedLanguages,
+            int strictness) {
+        // TODO: Implement engine selection code here.
+        Intent engineIntent = new Intent(
+        "android.intent.action.START_TTS_ENGINE");
+        if (engineName != null) {
+            engineIntent.addCategory("android.intent.action.tts_engine."
+                    + engineName);
+        }
+        for (int i = 0; i < requestedLanguages.length; i++) {
+            engineIntent.addCategory("android.intent.action.tts_lang."
+                    + requestedLanguages[i]);
+        }
+        ResolveInfo[] enginesArray = new ResolveInfo[0];
+        PackageManager pm = getPackageManager();
+        enginesArray = pm.queryIntentActivities(engineIntent, 0).toArray(
+                enginesArray);
+    }
+
+    private void setEngine(Intent engineIntent) {
+        // TODO: Implement engine selection code here.
+    }
+
+    private int getEngineStatus() {
+        // TODO: Proposal - add a sanity check method that
+        // TTS engine plugins must implement.
+        return 0;
+    }
+
+    /**
+     * Adds a sound resource to the TTS.
+     *
+     * @param text
+     *            The text that should be associated with the sound resource
+     * @param packageName
+     *            The name of the package which has the sound resource
+     * @param resId
+     *            The resource ID of the sound within its package
+     */
+    private void addSpeech(String text, String packageName, int resId) {
+        mUtterances.put(text, new SoundResource(packageName, resId));
+    }
+
+    /**
+     * Adds a sound resource to the TTS.
+     *
+     * @param text
+     *            The text that should be associated with the sound resource
+     * @param filename
+     *            The filename of the sound resource. This must be a complete
+     *            path like: (/sdcard/mysounds/mysoundbite.mp3).
+     */
+    private void addSpeech(String text, String filename) {
+        mUtterances.put(text, new SoundResource(filename));
+    }
+
+    /**
+     * Adds a sound resource to the TTS as an earcon.
+     *
+     * @param earcon
+     *            The text that should be associated with the sound resource
+     * @param packageName
+     *            The name of the package which has the sound resource
+     * @param resId
+     *            The resource ID of the sound within its package
+     */
+    private void addEarcon(String earcon, String packageName, int resId) {
+        mEarcons.put(earcon, new SoundResource(packageName, resId));
+    }
+
+    /**
+     * Adds a sound resource to the TTS as an earcon.
+     *
+     * @param earcon
+     *            The text that should be associated with the sound resource
+     * @param filename
+     *            The filename of the sound resource. This must be a complete
+     *            path like: (/sdcard/mysounds/mysoundbite.mp3).
+     */
+    private void addEarcon(String earcon, String filename) {
+        mEarcons.put(earcon, new SoundResource(filename));
+    }
+
+    /**
+     * Speaks the given text using the specified queueing mode and parameters.
+     *
+     * @param text
+     *            The text that should be spoken
+     * @param queueMode
+     *            0 for no queue (interrupts all previous utterances), 1 for
+     *            queued
+     * @param params
+     *            An ArrayList of parameters. This is not implemented for all
+     *            engines.
+     */
+    private void speak(String text, int queueMode, ArrayList<String> params) {
+        if (queueMode == 0) {
+            stop();
+        }
+        mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.SPEECH));
+        if (!mIsSpeaking) {
+            processSpeechQueue();
+        }
+    }
+
+    /**
+     * Plays the earcon using the specified queueing mode and parameters.
+     *
+     * @param earcon
+     *            The earcon that should be played
+     * @param queueMode
+     *            0 for no queue (interrupts all previous utterances), 1 for
+     *            queued
+     * @param params
+     *            An ArrayList of parameters. This is not implemented for all
+     *            engines.
+     */
+    private void playEarcon(String earcon, int queueMode,
+            ArrayList<String> params) {
+        if (queueMode == 0) {
+            stop();
+        }
+        mSpeechQueue.add(new SpeechItem(earcon, params, SpeechItem.EARCON));
+        if (!mIsSpeaking) {
+            processSpeechQueue();
+        }
+    }
+
+    /**
+     * Stops all speech output and removes any utterances still in the queue.
+     */
+    private void stop() {
+        Log.i("TTS", "Stopping");
+        mSpeechQueue.clear();
+
+        nativeSynth.stop();
+        mIsSpeaking = false;
+        if (mPlayer != null) {
+            try {
+                mPlayer.stop();
+            } catch (IllegalStateException e) {
+                // Do nothing, the player is already stopped.
+            }
+        }
+        Log.i("TTS", "Stopped");
+    }
+
+    public void onCompletion(MediaPlayer arg0) {
+        processSpeechQueue();
+    }
+
+    private void playSilence(long duration, int queueMode,
+            ArrayList<String> params) {
+        if (queueMode == 0) {
+            stop();
+        }
+        mSpeechQueue.add(new SpeechItem(duration));
+        if (!mIsSpeaking) {
+            processSpeechQueue();
+        }
+    }
+
+    private void silence(final long duration) {
+        class SilenceThread implements Runnable {
+            public void run() {
+                try {
+                    Thread.sleep(duration);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                } finally {
+                    processSpeechQueue();
+                }
+            }
+        }
+        Thread slnc = (new Thread(new SilenceThread()));
+        slnc.setPriority(Thread.MIN_PRIORITY);
+        slnc.start();
+    }
+
+    private void speakInternalOnly(final String text,
+            final ArrayList<String> params) {
+        class SynthThread implements Runnable {
+            public void run() {
+                boolean synthAvailable = false;
+                try {
+                    synthAvailable = synthesizerLock.tryLock();
+                    if (!synthAvailable) {
+                        Thread.sleep(100);
+                        Thread synth = (new Thread(new SynthThread()));
+                        synth.setPriority(Thread.MIN_PRIORITY);
+                        synth.start();
+                        return;
+                    }
+                    nativeSynth.speak(text);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                } finally {
+                    // This check is needed because finally will always run;
+                    // even if the
+                    // method returns somewhere in the try block.
+                    if (synthAvailable) {
+                        synthesizerLock.unlock();
+                    }
+                }
+            }
+        }
+        Thread synth = (new Thread(new SynthThread()));
+        synth.setPriority(Thread.MIN_PRIORITY);
+        synth.start();
+    }
+
+    private SoundResource getSoundResource(SpeechItem speechItem) {
+        SoundResource sr = null;
+        String text = speechItem.mText;
+        if (speechItem.mType == SpeechItem.SILENCE) {
+            // Do nothing if this is just silence
+        } else if (speechItem.mType == SpeechItem.EARCON) {
+            sr = mEarcons.get(text);
+        } else {
+            sr = mUtterances.get(text);
+        }
+        return sr;
+    }
+
+    private void dispatchSpeechCompletedCallbacks(String mark) {
+        Log.i("TTS callback", "dispatch started");
+        // Broadcast to all clients the new value.
+        final int N = mCallbacks.beginBroadcast();
+        for (int i = 0; i < N; i++) {
+            try {
+                mCallbacks.getBroadcastItem(i).markReached(mark);
+            } catch (RemoteException e) {
+                // The RemoteCallbackList will take care of removing
+                // the dead object for us.
+            }
+        }
+        mCallbacks.finishBroadcast();
+        Log.i("TTS callback", "dispatch completed to " + N);
+    }
+
+    private void processSpeechQueue() {
+        boolean speechQueueAvailable = false;
+        try {
+            speechQueueAvailable = speechQueueLock.tryLock();
+            if (!speechQueueAvailable) {
+                return;
+            }
+            if (mSpeechQueue.size() < 1) {
+                mIsSpeaking = false;
+                // Dispatch a completion here as this is the
+                // only place where speech completes normally.
+                // Nothing left to say in the queue is a special case
+                // that is always a "mark" - associated text is null.
+                dispatchSpeechCompletedCallbacks("");
+                return;
+            }
+
+            SpeechItem currentSpeechItem = mSpeechQueue.get(0);
+            mIsSpeaking = true;
+            SoundResource sr = getSoundResource(currentSpeechItem);
+            // Synth speech as needed - synthesizer should call
+            // processSpeechQueue to continue running the queue
+            Log.i("TTS processing: ", currentSpeechItem.mText);
+            if (sr == null) {
+                if (currentSpeechItem.mType == SpeechItem.SPEECH) {
+                    // TODO: Split text up into smaller chunks before accepting
+                    // them
+                    // for processing.
+                    speakInternalOnly(currentSpeechItem.mText,
+                            currentSpeechItem.mParams);
+                } else {
+                    // This is either silence or an earcon that was missing
+                    silence(currentSpeechItem.mDuration);
+                }
+            } else {
+                cleanUpPlayer();
+                if (sr.mSourcePackageName == PKGNAME) {
+                    // Utterance is part of the TTS library
+                    mPlayer = MediaPlayer.create(this, sr.mResId);
+                } else if (sr.mSourcePackageName != null) {
+                    // Utterance is part of the app calling the library
+                    Context ctx;
+                    try {
+                        ctx = this.createPackageContext(sr.mSourcePackageName,
+                                0);
+                    } catch (NameNotFoundException e) {
+                        e.printStackTrace();
+                        mSpeechQueue.remove(0); // Remove it from the queue and
+                        // move on
+                        mIsSpeaking = false;
+                        return;
+                    }
+                    mPlayer = MediaPlayer.create(ctx, sr.mResId);
+                } else {
+                    // Utterance is coming from a file
+                    mPlayer = MediaPlayer.create(this, Uri.parse(sr.mFilename));
+                }
+
+                // Check if Media Server is dead; if it is, clear the queue and
+                // give up for now - hopefully, it will recover itself.
+                if (mPlayer == null) {
+                    mSpeechQueue.clear();
+                    mIsSpeaking = false;
+                    return;
+                }
+                mPlayer.setOnCompletionListener(this);
+                try {
+                    mPlayer.start();
+                } catch (IllegalStateException e) {
+                    mSpeechQueue.clear();
+                    mIsSpeaking = false;
+                    cleanUpPlayer();
+                    return;
+                }
+            }
+            if (mSpeechQueue.size() > 0) {
+                mSpeechQueue.remove(0);
+            }
+        } finally {
+            // This check is needed because finally will always run; even if the
+            // method returns somewhere in the try block.
+            if (speechQueueAvailable) {
+                speechQueueLock.unlock();
+            }
+        }
+    }
+
+    private void cleanUpPlayer() {
+        if (mPlayer != null) {
+            mPlayer.release();
+            mPlayer = null;
+        }
+    }
+
+    /**
+     * Synthesizes the given text using the specified queuing mode and
+     * parameters.
+     *
+     * @param text
+     *            The String of text that should be synthesized
+     * @param params
+     *            An ArrayList of parameters. The first element of this array
+     *            controls the type of voice to use.
+     * @param filename
+     *            The string that gives the full output filename; it should be
+     *            something like "/sdcard/myappsounds/mysound.wav".
+     * @return A boolean that indicates if the synthesis succeeded
+     */
+    private boolean synthesizeToFile(String text, ArrayList<String> params,
+            String filename, boolean calledFromApi) {
+        // Only stop everything if this is a call made by an outside app trying
+        // to
+        // use the API. Do NOT stop if this is a call from within the service as
+        // clearing the speech queue here would be a mistake.
+        if (calledFromApi) {
+            stop();
+        }
+        Log.i("TTS", "Synthesizing to " + filename);
+        boolean synthAvailable = false;
+        try {
+            synthAvailable = synthesizerLock.tryLock();
+            if (!synthAvailable) {
+                return false;
+            }
+            // Don't allow a filename that is too long
+            // TODO use platform constant
+            if (filename.length() > 250) {
+                return false;
+            }
+            nativeSynth.synthesizeToFile(text, filename);
+        } finally {
+            // This check is needed because finally will always run; even if the
+            // method returns somewhere in the try block.
+            if (synthAvailable) {
+                synthesizerLock.unlock();
+            }
+        }
+        Log.i("TTS", "Completed synthesis for " + filename);
+        return true;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (ACTION.equals(intent.getAction())) {
+            for (String category : intent.getCategories()) {
+                if (category.equals(CATEGORY)) {
+                    return mBinder;
+                }
+            }
+        }
+        return null;
+    }
+
+    private final ITts.Stub mBinder = new Stub() {
+
+        public void registerCallback(ITtsCallback cb) {
+            if (cb != null)
+                mCallbacks.register(cb);
+        }
+
+        public void unregisterCallback(ITtsCallback cb) {
+            if (cb != null)
+                mCallbacks.unregister(cb);
+        }
+
+        /**
+         * Gives a hint about the type of engine that is preferred.
+         *
+         * @param selectedEngine
+         *            The TTS engine that should be used
+         */
+        public void setEngine(String engineName, String[] supportedLanguages,
+                int strictness) {
+            mSelf.setEngine(engineName, supportedLanguages, strictness);
+        }
+
+        /**
+         * Specifies exactly what the engine has to support. Will always be
+         * considered "strict"; can be used for implementing
+         * optional/experimental features that are not supported by all engines.
+         *
+         * @param engineIntent
+         *            An intent that specifies exactly what the engine has to
+         *            support.
+         */
+        public void setEngineWithIntent(Intent engineIntent) {
+            mSelf.setEngine(engineIntent);
+        }
+
+        /**
+         * Speaks the given text using the specified queueing mode and
+         * parameters.
+         *
+         * @param text
+         *            The text that should be spoken
+         * @param queueMode
+         *            0 for no queue (interrupts all previous utterances), 1 for
+         *            queued
+         * @param params
+         *            An ArrayList of parameters. The first element of this
+         *            array controls the type of voice to use.
+         */
+        public void speak(String text, int queueMode, String[] params) {
+            ArrayList<String> speakingParams = new ArrayList<String>();
+            if (params != null) {
+                speakingParams = new ArrayList<String>(Arrays.asList(params));
+            }
+            mSelf.speak(text, queueMode, speakingParams);
+        }
+
+        /**
+         * Plays the earcon using the specified queueing mode and parameters.
+         *
+         * @param earcon
+         *            The earcon that should be played
+         * @param queueMode
+         *            0 for no queue (interrupts all previous utterances), 1 for
+         *            queued
+         * @param params
+         *            An ArrayList of parameters.
+         */
+        public void playEarcon(String earcon, int queueMode, String[] params) {
+            ArrayList<String> speakingParams = new ArrayList<String>();
+            if (params != null) {
+                speakingParams = new ArrayList<String>(Arrays.asList(params));
+            }
+            mSelf.playEarcon(earcon, queueMode, speakingParams);
+        }
+
+        /**
+         * Plays the silence using the specified queueing mode and parameters.
+         *
+         * @param duration
+         *            The duration of the silence that should be played
+         * @param queueMode
+         *            0 for no queue (interrupts all previous utterances), 1 for
+         *            queued
+         * @param params
+         *            An ArrayList of parameters.
+         */
+        public void playSilence(long duration, int queueMode, String[] params) {
+            ArrayList<String> speakingParams = new ArrayList<String>();
+            if (params != null) {
+                speakingParams = new ArrayList<String>(Arrays.asList(params));
+            }
+            mSelf.playSilence(duration, queueMode, speakingParams);
+        }
+
+
+        /**
+         * Stops all speech output and removes any utterances still in the
+         * queue.
+         */
+        public void stop() {
+            mSelf.stop();
+        }
+
+        /**
+         * Returns whether or not the TTS is speaking.
+         *
+         * @return Boolean to indicate whether or not the TTS is speaking
+         */
+        public boolean isSpeaking() {
+            return (mSelf.mIsSpeaking && (mSpeechQueue.size() < 1));
+        }
+
+        /**
+         * Adds a sound resource to the TTS.
+         *
+         * @param text
+         *            The text that should be associated with the sound resource
+         * @param packageName
+         *            The name of the package which has the sound resource
+         * @param resId
+         *            The resource ID of the sound within its package
+         */
+        public void addSpeech(String text, String packageName, int resId) {
+            mSelf.addSpeech(text, packageName, resId);
+        }
+
+        /**
+         * Adds a sound resource to the TTS.
+         *
+         * @param text
+         *            The text that should be associated with the sound resource
+         * @param filename
+         *            The filename of the sound resource. This must be a
+         *            complete path like: (/sdcard/mysounds/mysoundbite.mp3).
+         */
+        public void addSpeechFile(String text, String filename) {
+            mSelf.addSpeech(text, filename);
+        }
+
+        /**
+         * Adds a sound resource to the TTS as an earcon.
+         *
+         * @param earcon
+         *            The text that should be associated with the sound resource
+         * @param packageName
+         *            The name of the package which has the sound resource
+         * @param resId
+         *            The resource ID of the sound within its package
+         */
+        public void addEarcon(String earcon, String packageName, int resId) {
+            mSelf.addEarcon(earcon, packageName, resId);
+        }
+
+        /**
+         * Adds a sound resource to the TTS as an earcon.
+         *
+         * @param earcon
+         *            The text that should be associated with the sound resource
+         * @param filename
+         *            The filename of the sound resource. This must be a
+         *            complete path like: (/sdcard/mysounds/mysoundbite.mp3).
+         */
+        public void addEarconFile(String earcon, String filename) {
+            mSelf.addEarcon(earcon, filename);
+        }
+
+        /**
+         * Sets the speech rate for the TTS. Note that this will only have an
+         * effect on synthesized speech; it will not affect pre-recorded speech.
+         *
+         * @param speechRate
+         *            The speech rate that should be used
+         */
+        public void setSpeechRate(int speechRate) {
+            mSelf.setSpeechRate(speechRate);
+        }
+
+        // TODO: Fix comment about language
+        /**
+         * Sets the speech rate for the TTS. Note that this will only have an
+         * effect on synthesized speech; it will not affect pre-recorded speech.
+         *
+         * @param language
+         *            The language to be used. The languages are specified by
+         *            their IETF language tags as defined by BCP 47. This is the
+         *            same standard used for the lang attribute in HTML. See:
+         *            http://en.wikipedia.org/wiki/IETF_language_tag
+         */
+        public void setLanguage(String language) {
+            mSelf.setLanguage(language);
+        }
+
+        /**
+         * Speaks the given text using the specified queueing mode and
+         * parameters.
+         *
+         * @param text
+         *            The String of text that should be synthesized
+         * @param params
+         *            An ArrayList of parameters. The first element of this
+         *            array controls the type of voice to use.
+         * @param filename
+         *            The string that gives the full output filename; it should
+         *            be something like "/sdcard/myappsounds/mysound.wav".
+         * @return A boolean that indicates if the synthesis succeeded
+         */
+        public boolean synthesizeToFile(String text, String[] params,
+                String filename) {
+            ArrayList<String> speakingParams = new ArrayList<String>();
+            if (params != null) {
+                speakingParams = new ArrayList<String>(Arrays.asList(params));
+            }
+            return mSelf.synthesizeToFile(text, speakingParams, filename, true);
+        }
+    };
+
+}
diff --git a/tts/jni/Android.mk b/tts/jni/Android.mk
new file mode 100755
index 0000000..665d6d2
--- /dev/null
+++ b/tts/jni/Android.mk
@@ -0,0 +1,31 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	android_tts_SynthProxy.cpp
+
+LOCAL_C_INCLUDES += \
+	$(JNI_H_INCLUDE)
+
+LOCAL_SHARED_LIBRARIES := \
+	libandroid_runtime \
+	libnativehelper \
+	libmedia \
+	libutils \
+	libcutils
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+
+LOCAL_MODULE:= libttssynthproxy
+
+LOCAL_ARM_MODE := arm
+
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/tts/jni/android_tts_SynthProxy.cpp b/tts/jni/android_tts_SynthProxy.cpp
new file mode 100755
index 0000000..d8f1bf3
--- /dev/null
+++ b/tts/jni/android_tts_SynthProxy.cpp
@@ -0,0 +1,595 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <stdio.h>
+#include <unistd.h>
+
+#define LOG_TAG "SynthProxy"
+
+#include <utils/Log.h>
+#include <nativehelper/jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <tts/TtsEngine.h>
+#include <media/AudioTrack.h>
+
+#include <dlfcn.h>
+
+#define DEFAULT_TTS_RATE        16000
+#define DEFAULT_TTS_FORMAT      AudioSystem::PCM_16_BIT
+#define DEFAULT_TTS_NB_CHANNELS 1
+
+#define USAGEMODE_PLAY_IMMEDIATELY 0
+#define USAGEMODE_WRITE_TO_FILE    1
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+struct fields_t {
+    jfieldID    synthProxyFieldJniData;
+    jclass      synthProxyClass;
+    jmethodID   synthProxyMethodPost;
+};
+
+struct afterSynthData_t {
+    jint jniStorage;
+    int  usageMode;
+    FILE* outputFile;
+};
+
+// ----------------------------------------------------------------------------
+static fields_t javaTTSFields;
+
+// ----------------------------------------------------------------------------
+class SynthProxyJniStorage {
+    public :
+        //jclass                    tts_class;
+        jobject                   tts_ref;
+        TtsEngine*                mNativeSynthInterface;
+        AudioTrack*               mAudioOut;
+        uint32_t                  mSampleRate;
+        AudioSystem::audio_format mAudFormat;
+        int                       mNbChannels;
+
+        SynthProxyJniStorage() {
+            //tts_class = NULL;
+            tts_ref = NULL;
+            mNativeSynthInterface = NULL;
+            mAudioOut = NULL;
+            mSampleRate = DEFAULT_TTS_RATE;
+            mAudFormat  = DEFAULT_TTS_FORMAT;
+            mNbChannels = DEFAULT_TTS_NB_CHANNELS;
+        }
+
+        ~SynthProxyJniStorage() {
+            killAudio();
+            if (mNativeSynthInterface) {
+                mNativeSynthInterface->shutdown();
+                mNativeSynthInterface = NULL;
+            }
+        }
+
+        void killAudio() {
+            if (mAudioOut) {
+                mAudioOut->stop();
+                delete mAudioOut;
+                mAudioOut = NULL;
+            }
+        }
+
+        void createAudioOut(uint32_t rate, AudioSystem::audio_format format,
+                int channel) {
+            mSampleRate = rate;
+            mAudFormat  = format;
+            mNbChannels = channel;
+
+            // TODO use the TTS stream type
+            int streamType = AudioSystem::MUSIC;
+
+            // retrieve system properties to ensure successful creation of the
+            // AudioTrack object for playback
+            int afSampleRate;
+            if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
+                afSampleRate = 44100;
+            }
+            int afFrameCount;
+            if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
+                afFrameCount = 2048;
+            }
+            uint32_t afLatency;
+            if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) {
+                afLatency = 500;
+            }
+            uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
+            if (minBufCount < 2) minBufCount = 2;
+            int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate;
+
+            mAudioOut = new AudioTrack(streamType, rate, format, channel,
+                    minFrameCount > 4096 ? minFrameCount : 4096,
+                    0, 0, 0, 0); // not using an AudioTrack callback
+
+            if (mAudioOut->initCheck() != NO_ERROR) {
+              LOGI("AudioTrack error");
+              delete mAudioOut;
+              mAudioOut = NULL;
+            } else {
+              LOGI("AudioTrack OK");
+              mAudioOut->start();
+              LOGI("AudioTrack started");
+            }
+        }
+};
+
+
+// ----------------------------------------------------------------------------
+void prepAudioTrack(SynthProxyJniStorage* pJniData,
+        uint32_t rate, AudioSystem::audio_format format, int channel)
+{
+    // Don't bother creating a new audiotrack object if the current
+    // object is already set.
+    if ( pJniData->mAudioOut &&
+         (rate == pJniData->mSampleRate) &&
+         (format == pJniData->mAudFormat) &&
+         (channel == pJniData->mNbChannels) ){
+        return;
+    }
+    if (pJniData->mAudioOut){
+        pJniData->killAudio();
+    }
+    pJniData->createAudioOut(rate, format, channel);
+}
+
+
+// ----------------------------------------------------------------------------
+/*
+ * Callback from TTS engine.
+ * Directly speaks using AudioTrack or write to file
+ */
+static void ttsSynthDoneCB(void * userdata, uint32_t rate,
+                           AudioSystem::audio_format format, int channel,
+                           int8_t *wav, size_t bufferSize) {
+    LOGI("ttsSynthDoneCallback: %d bytes", bufferSize);
+
+    afterSynthData_t* pForAfter = (afterSynthData_t*)userdata;
+
+    if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY){
+        LOGI("Direct speech");
+
+        if (wav == NULL) {
+            LOGI("Null: speech has completed");
+        }
+
+        if (bufferSize > 0) {
+            SynthProxyJniStorage* pJniData =
+                    (SynthProxyJniStorage*)(pForAfter->jniStorage);
+            prepAudioTrack(pJniData, rate, format, channel);
+            if (pJniData->mAudioOut) {
+                pJniData->mAudioOut->write(wav, bufferSize);
+                LOGI("AudioTrack wrote: %d bytes", bufferSize);
+            } else {
+                LOGI("Can't play, null audiotrack");
+            }
+        }
+    } else  if (pForAfter->usageMode == USAGEMODE_WRITE_TO_FILE) {
+        LOGI("Save to file");
+        if (wav == NULL) {
+            LOGI("Null: speech has completed");
+        }
+        if (bufferSize > 0){
+            fwrite(wav, 1, bufferSize, pForAfter->outputFile);
+        }
+    }
+    // TODO update to call back into the SynthProxy class through the
+    //      javaTTSFields.synthProxyMethodPost methode to notify
+    //      playback has completed
+
+    delete pForAfter;
+    return;
+}
+
+
+// ----------------------------------------------------------------------------
+static void
+android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
+        jobject weak_this, jstring nativeSoLib)
+{
+    SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage();
+
+    prepAudioTrack(pJniStorage,
+            DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
+
+    const char *nativeSoLibNativeString =
+            env->GetStringUTFChars(nativeSoLib, 0);
+
+    void *engine_lib_handle = dlopen(nativeSoLibNativeString,
+            RTLD_NOW | RTLD_LOCAL);
+    if (engine_lib_handle==NULL) {
+       LOGI("engine_lib_handle==NULL");
+       // TODO report error so the TTS can't be used
+    } else {
+        TtsEngine *(*get_TtsEngine)() =
+            reinterpret_cast<TtsEngine* (*)()>(dlsym(engine_lib_handle, "getTtsEngine"));
+        pJniStorage->mNativeSynthInterface = (*get_TtsEngine)();
+        if (pJniStorage->mNativeSynthInterface) {
+            pJniStorage->mNativeSynthInterface->init(ttsSynthDoneCB);
+        }
+    }
+
+    // we use a weak reference so the SynthProxy object can be garbage collected.
+    pJniStorage->tts_ref = env->NewGlobalRef(weak_this);
+
+    // save the JNI resources so we can use them (and free them) later
+    env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData,
+            (int)pJniStorage);
+
+    env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString);
+}
+
+
+static void
+android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData) {
+        SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+        delete pSynthData;
+    }
+}
+
+
+static void
+android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
+        jstring language)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_setLanguage(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    const char *langNativeString = env->GetStringUTFChars(language, 0);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->setLanguage(langNativeString,
+                strlen(langNativeString));
+    }
+    env->ReleaseStringUTFChars(language, langNativeString);
+}
+
+
+static void
+android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData,
+        int speechRate)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_setSpeechRate(): invalid JNI data");
+        return;
+    }
+
+    int bufSize = 10;
+    char buffer [bufSize];
+    sprintf(buffer, "%d", speechRate);
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    LOGI("setting speech rate to %d", speechRate);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->setProperty("rate", buffer, bufSize);
+    }
+}
+
+
+// TODO: Refactor this to get rid of any assumptions about sample rate, etc.
+static void
+android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
+        jstring textJavaString, jstring filenameJavaString)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+
+    const char *filenameNativeString =
+            env->GetStringUTFChars(filenameJavaString, 0);
+    const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
+
+    afterSynthData_t* pForAfter = new (afterSynthData_t);
+    pForAfter->jniStorage = jniData;
+    pForAfter->usageMode  = USAGEMODE_WRITE_TO_FILE;
+
+    pForAfter->outputFile = fopen(filenameNativeString, "wb");
+
+    // Write 44 blank bytes for WAV header, then come back and fill them in
+    // after we've written the audio data
+    char header[44];
+    fwrite(header, 1, 44, pForAfter->outputFile);
+
+    unsigned int unique_identifier;
+
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->synthesizeText(textNativeString,
+                (void *)pForAfter);
+    }
+
+    long filelen = ftell(pForAfter->outputFile);
+
+    int samples = (((int)filelen) - 44) / 2;
+    header[0] = 'R';
+    header[1] = 'I';
+    header[2] = 'F';
+    header[3] = 'F';
+    ((uint32_t *)(&header[4]))[0] = filelen - 8;
+    header[8] = 'W';
+    header[9] = 'A';
+    header[10] = 'V';
+    header[11] = 'E';
+
+    header[12] = 'f';
+    header[13] = 'm';
+    header[14] = 't';
+    header[15] = ' ';
+
+    ((uint32_t *)(&header[16]))[0] = 16;  // size of fmt
+
+    ((unsigned short *)(&header[20]))[0] = 1;  // format
+    ((unsigned short *)(&header[22]))[0] = 1;  // channels
+    ((uint32_t *)(&header[24]))[0] = 22050;  // samplerate
+    ((uint32_t *)(&header[28]))[0] = 44100;  // byterate
+    ((unsigned short *)(&header[32]))[0] = 2;  // block align
+    ((unsigned short *)(&header[34]))[0] = 16;  // bits per sample
+
+    header[36] = 'd';
+    header[37] = 'a';
+    header[38] = 't';
+    header[39] = 'a';
+
+    ((uint32_t *)(&header[40]))[0] = samples * 2;  // size of data
+
+    // Skip back to the beginning and rewrite the header
+    fseek(pForAfter->outputFile, 0, SEEK_SET);
+    fwrite(header, 1, 44, pForAfter->outputFile);
+
+    fflush(pForAfter->outputFile);
+    fclose(pForAfter->outputFile);
+
+    env->ReleaseStringUTFChars(textJavaString, textNativeString);
+    env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString);
+}
+
+
+static void
+android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
+        jstring textJavaString)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_speak(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+
+    if (pSynthData->mAudioOut) {
+        pSynthData->mAudioOut->stop();
+        pSynthData->mAudioOut->start();
+    }
+
+    afterSynthData_t* pForAfter = new (afterSynthData_t);
+    pForAfter->jniStorage = jniData;
+    pForAfter->usageMode  = USAGEMODE_PLAY_IMMEDIATELY;
+
+    if (pSynthData->mNativeSynthInterface) {
+        const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
+        pSynthData->mNativeSynthInterface->synthesizeText(textNativeString,
+                (void *)pForAfter);
+        env->ReleaseStringUTFChars(textJavaString, textNativeString);
+    }
+}
+
+
+static void
+android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->stop();
+    }
+    if (pSynthData->mAudioOut) {
+        pSynthData->mAudioOut->stop();
+    }
+}
+
+
+static void
+android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_shutdown(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->shutdown();
+        pSynthData->mNativeSynthInterface = NULL;
+    }
+}
+
+
+// TODO add buffer format
+static void
+android_tts_SynthProxy_playAudioBuffer(JNIEnv *env, jobject thiz, jint jniData,
+        int bufferPointer, int bufferSize)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_playAudioBuffer(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    short* wav = (short*) bufferPointer;
+    pSynthData->mAudioOut->write(wav, bufferSize);
+    LOGI("AudioTrack wrote: %d bytes", bufferSize);
+}
+
+
+JNIEXPORT jstring JNICALL
+android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_getLanguage(): invalid JNI data");
+        return env->NewStringUTF("");
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    size_t bufSize = 100;
+    char buf[bufSize];
+    memset(buf, 0, bufSize);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->getLanguage(buf, &bufSize);
+    }
+    return env->NewStringUTF(buf);
+}
+
+JNIEXPORT int JNICALL
+android_tts_SynthProxy_getRate(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_getRate(): invalid JNI data");
+        return 0;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    size_t bufSize = 100;
+
+    char buf[bufSize];
+    memset(buf, 0, bufSize);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->getProperty("rate", buf, &bufSize);
+    }
+    return atoi(buf);
+}
+
+// Dalvik VM type signatures
+static JNINativeMethod gMethods[] = {
+    {   "native_stop",
+        "(I)V",
+        (void*)android_tts_SynthProxy_stop
+    },
+    {   "native_speak",
+        "(ILjava/lang/String;)V",
+        (void*)android_tts_SynthProxy_speak
+    },
+    {   "native_synthesizeToFile",
+        "(ILjava/lang/String;Ljava/lang/String;)V",
+        (void*)android_tts_SynthProxy_synthesizeToFile
+    },
+    {   "native_setLanguage",
+        "(ILjava/lang/String;)V",
+        (void*)android_tts_SynthProxy_setLanguage
+    },
+    {   "native_setSpeechRate",
+        "(II)V",
+        (void*)android_tts_SynthProxy_setSpeechRate
+    },
+    {   "native_playAudioBuffer",
+        "(III)V",
+        (void*)android_tts_SynthProxy_playAudioBuffer
+    },
+    {   "native_getLanguage",
+        "(I)Ljava/lang/String;",
+        (void*)android_tts_SynthProxy_getLanguage
+    },
+    {   "native_getRate",
+        "(I)I",
+        (void*)android_tts_SynthProxy_getRate
+    },
+    {   "native_shutdown",
+        "(I)V",
+        (void*)android_tts_SynthProxy_shutdown
+    },
+    {   "native_setup",
+        "(Ljava/lang/Object;Ljava/lang/String;)V",
+        (void*)android_tts_SynthProxy_native_setup
+    },
+    {   "native_finalize",
+        "(I)V",
+        (void*)android_tts_SynthProxy_native_finalize
+    }
+};
+
+#define SP_JNIDATA_FIELD_NAME                "mJniData"
+#define SP_POSTSPEECHSYNTHESIZED_METHOD_NAME "postNativeSpeechSynthesizedInJava"
+
+// TODO: verify this is the correct path
+static const char* const kClassPathName = "android/tts/SynthProxy";
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+    JNIEnv* env = NULL;
+    jint result = -1;
+    jclass clazz;
+
+    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        LOGE("ERROR: GetEnv failed\n");
+        goto bail;
+    }
+    assert(env != NULL);
+
+    clazz = env->FindClass(kClassPathName);
+    if (clazz == NULL) {
+        LOGE("Can't find %s", kClassPathName);
+        goto bail;
+    }
+
+    javaTTSFields.synthProxyClass = clazz;
+    javaTTSFields.synthProxyFieldJniData = NULL;
+    javaTTSFields.synthProxyMethodPost = NULL;
+
+    javaTTSFields.synthProxyFieldJniData = env->GetFieldID(clazz,
+            SP_JNIDATA_FIELD_NAME, "I");
+    if (javaTTSFields.synthProxyFieldJniData == NULL) {
+        LOGE("Can't find %s.%s field", kClassPathName, SP_JNIDATA_FIELD_NAME);
+        goto bail;
+    }
+
+    javaTTSFields.synthProxyMethodPost = env->GetStaticMethodID(clazz,
+            SP_POSTSPEECHSYNTHESIZED_METHOD_NAME, "(Ljava/lang/Object;II)V");
+    if (javaTTSFields.synthProxyMethodPost == NULL) {
+        LOGE("Can't find %s.%s method", kClassPathName, SP_POSTSPEECHSYNTHESIZED_METHOD_NAME);
+        goto bail;
+    }
+
+    if (jniRegisterNativeMethods(
+            env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
+        goto bail;
+
+    /* success -- return valid version number */
+    result = JNI_VERSION_1_4;
+
+ bail:
+    return result;
+}