Merge change 9004

* changes:
  Fix NullPointerException when checking if its a sinkDevice.
diff --git a/api/4.xml b/api/4.xml
index c8a2e83..cac3ae8 100644
--- a/api/4.xml
+++ b/api/4.xml
@@ -256713,7 +256713,7 @@
  extends="java.lang.Enum"
  abstract="false"
  static="false"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -258766,7 +258766,7 @@
 <package name="java.util.concurrent.locks"
 >
 <class name="AbstractQueuedSynchronizer"
- extends="java.lang.Object"
+ extends="java.util.concurrent.locks.AbstractOwnableSynchronizer"
  abstract="true"
  static="false"
  final="false"
diff --git a/api/current.xml b/api/current.xml
index c819972..b1e9c83 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -1024,17 +1024,6 @@
  visibility="public"
 >
 </field>
-<field name="SHUTDOWN"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.permission.SHUTDOWN&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="SIGNAL_PERSISTENT_PROCESSES"
  type="java.lang.String"
  transient="false"
@@ -1057,17 +1046,6 @@
  visibility="public"
 >
 </field>
-<field name="STOP_APP_SWITCHES"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.permission.STOP_APP_SWITCHES&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="SUBSCRIBED_FEEDS_READ"
  type="java.lang.String"
  transient="false"
@@ -31763,17 +31741,6 @@
  visibility="public"
 >
 </field>
-<field name="BACKUP_SERVICE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;backup&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="BIND_AUTO_CREATE"
  type="int"
  transient="false"
@@ -32619,19 +32586,6 @@
 <parameter name="mode" type="int">
 </parameter>
 </method>
-<method name="getSharedPrefsFile"
- return="java.io.File"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="name" type="java.lang.String">
-</parameter>
-</method>
 <method name="getSystemService"
  return="java.lang.Object"
  abstract="false"
@@ -33426,70 +33380,6 @@
 </exception>
 </method>
 </interface>
-<interface name="IIntentReceiver"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.os.IInterface">
-</implements>
-<method name="performReceive"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="intent" type="android.content.Intent">
-</parameter>
-<parameter name="resultCode" type="int">
-</parameter>
-<parameter name="data" type="java.lang.String">
-</parameter>
-<parameter name="extras" type="android.os.Bundle">
-</parameter>
-<parameter name="ordered" type="boolean">
-</parameter>
-<exception name="RemoteException" type="android.os.RemoteException">
-</exception>
-</method>
-</interface>
-<interface name="IIntentSender"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.os.IInterface">
-</implements>
-<method name="send"
- return="int"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="code" type="int">
-</parameter>
-<parameter name="intent" type="android.content.Intent">
-</parameter>
-<parameter name="resolvedType" type="java.lang.String">
-</parameter>
-<parameter name="finishedReceiver" type="android.content.IIntentReceiver">
-</parameter>
-<exception name="RemoteException" type="android.os.RemoteException">
-</exception>
-</method>
-</interface>
 <class name="Intent"
  extends="java.lang.Object"
  abstract="false"
@@ -37683,26 +37573,6 @@
 >
 <implements name="android.os.Parcelable">
 </implements>
-<constructor name="IntentSender"
- type="android.content.IntentSender"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="target" type="android.content.IIntentSender">
-</parameter>
-</constructor>
-<constructor name="IntentSender"
- type="android.content.IntentSender"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="target" type="android.os.IBinder">
-</parameter>
-</constructor>
 <method name="describeContents"
  return="int"
  abstract="false"
@@ -51230,17 +51100,6 @@
 <parameter name="entryName" type="java.lang.String">
 </parameter>
 </method>
-<method name="getLearner"
- return="android.gesture.Learner"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getOrientationStyle"
  return="int"
  abstract="false"
@@ -52509,15 +52368,6 @@
 >
 </field>
 </class>
-<class name="Learner"
- extends="java.lang.Object"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility=""
->
-</class>
 <class name="OrientedBoundingBox"
  extends="java.lang.Object"
  abstract="false"
@@ -123165,19 +123015,6 @@
 <parameter name="mode" type="int">
 </parameter>
 </method>
-<method name="getSharedPrefsFile"
- return="java.io.File"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="name" type="java.lang.String">
-</parameter>
-</method>
 <method name="getSystemService"
  return="java.lang.Object"
  abstract="false"
@@ -146942,7 +146779,7 @@
  value="2"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -146968,6 +146805,17 @@
  visibility="public"
 >
 </field>
+<field name="SURFACE_FROZEN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SURFACE_HIDDEN"
  type="int"
  transient="false"
@@ -163493,6 +163341,8 @@
 </parameter>
 <parameter name="currentQuota" type="long">
 </parameter>
+<parameter name="totalUsedQuota" type="long">
+</parameter>
 <parameter name="quotaUpdater" type="android.webkit.WebStorage.QuotaUpdater">
 </parameter>
 </method>
@@ -165153,6 +165003,8 @@
 </parameter>
 <parameter name="currentQuota" type="long">
 </parameter>
+<parameter name="totalUsedQuota" type="long">
+</parameter>
 <parameter name="quotaUpdater" type="android.webkit.WebStorage.QuotaUpdater">
 </parameter>
 </method>
@@ -193475,7 +193327,7 @@
 <method name="valid"
  return="boolean"
  abstract="false"
- native="true"
+ native="false"
  synchronized="false"
  static="false"
  final="false"
@@ -198598,7 +198450,7 @@
  return="java.nio.channels.FileChannel"
  abstract="false"
  native="false"
- synchronized="false"
+ synchronized="true"
  static="false"
  final="true"
  deprecated="not deprecated"
@@ -207468,7 +207320,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="i" type="int">
+<parameter name="value" type="int">
 </parameter>
 </method>
 <method name="toString"
@@ -208268,10 +208120,10 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="y" type="double">
-</parameter>
 <parameter name="x" type="double">
 </parameter>
+<parameter name="y" type="double">
+</parameter>
 </method>
 <method name="cbrt"
  return="double"
@@ -273628,7 +273480,7 @@
  return="void"
  abstract="false"
  native="false"
- synchronized="true"
+ synchronized="false"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -273641,7 +273493,7 @@
  return="void"
  abstract="false"
  native="false"
- synchronized="true"
+ synchronized="false"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -273652,7 +273504,7 @@
  return="int"
  abstract="false"
  native="false"
- synchronized="true"
+ synchronized="false"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -273687,7 +273539,7 @@
  return="boolean"
  abstract="false"
  native="false"
- synchronized="true"
+ synchronized="false"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -273722,7 +273574,7 @@
  return="void"
  abstract="false"
  native="false"
- synchronized="true"
+ synchronized="false"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -276067,7 +275919,7 @@
  return="E"
  abstract="false"
  native="false"
- synchronized="true"
+ synchronized="false"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -277433,7 +277285,7 @@
  return="E"
  abstract="false"
  native="false"
- synchronized="true"
+ synchronized="false"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -277913,7 +277765,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 </method>
 <method name="offer"
@@ -277926,7 +277778,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 <parameter name="timeout" type="long">
 </parameter>
@@ -277984,7 +277836,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 <exception name="InterruptedException" type="java.lang.InterruptedException">
 </exception>
@@ -278044,7 +277896,20 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
+</parameter>
+</method>
+<method name="contains"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="o" type="java.lang.Object">
 </parameter>
 </method>
 <method name="drainTo"
@@ -278085,7 +277950,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 </method>
 <method name="offer"
@@ -278098,7 +277963,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 <parameter name="timeout" type="long">
 </parameter>
@@ -278134,7 +277999,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 <exception name="InterruptedException" type="java.lang.InterruptedException">
 </exception>
@@ -278150,6 +278015,19 @@
  visibility="public"
 >
 </method>
+<method name="remove"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="o" type="java.lang.Object">
+</parameter>
+</method>
 <method name="take"
  return="E"
  abstract="true"
@@ -278367,7 +278245,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="t" type="java.util.Map&lt;? extends K, ? extends V&gt;">
+<parameter name="m" type="java.util.Map&lt;? extends K, ? extends V&gt;">
 </parameter>
 </constructor>
 <method name="contains"
@@ -278530,7 +278408,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 </method>
 <method name="peek"
@@ -278680,7 +278558,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="array" type="E[]">
+<parameter name="toCopyIn" type="E[]">
 </parameter>
 </constructor>
 <method name="add"
@@ -278708,7 +278586,7 @@
 >
 <parameter name="index" type="int">
 </parameter>
-<parameter name="e" type="E">
+<parameter name="element" type="E">
 </parameter>
 </method>
 <method name="addAll"
@@ -278836,9 +278714,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="e" type="E">
-</parameter>
-<parameter name="index" type="int">
+<parameter name="o" type="java.lang.Object">
 </parameter>
 </method>
 <method name="indexOf"
@@ -278851,7 +278727,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="java.lang.Object">
+<parameter name="e" type="E">
+</parameter>
+<parameter name="index" type="int">
 </parameter>
 </method>
 <method name="isEmpty"
@@ -278886,9 +278764,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="e" type="E">
-</parameter>
-<parameter name="index" type="int">
+<parameter name="o" type="java.lang.Object">
 </parameter>
 </method>
 <method name="lastIndexOf"
@@ -278901,7 +278777,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="java.lang.Object">
+<parameter name="e" type="E">
+</parameter>
+<parameter name="index" type="int">
 </parameter>
 </method>
 <method name="listIterator"
@@ -278992,7 +278870,7 @@
 >
 <parameter name="index" type="int">
 </parameter>
-<parameter name="e" type="E">
+<parameter name="element" type="E">
 </parameter>
 </method>
 <method name="size"
@@ -279356,7 +279234,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 </method>
 <method name="offer"
@@ -279369,7 +279247,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 <parameter name="timeout" type="long">
 </parameter>
@@ -279397,12 +279275,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="time" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
 </method>
 <method name="poll"
  return="E"
@@ -279414,6 +279286,12 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="timeout" type="long">
+</parameter>
+<parameter name="unit" type="java.util.concurrent.TimeUnit">
+</parameter>
+<exception name="InterruptedException" type="java.lang.InterruptedException">
+</exception>
 </method>
 <method name="put"
  return="void"
@@ -279425,7 +279303,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 </method>
 <method name="remainingCapacity"
@@ -280496,7 +280374,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 <parameter name="timeout" type="long">
 </parameter>
@@ -280515,7 +280393,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 </method>
 <method name="peek"
@@ -280567,7 +280445,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 <exception name="InterruptedException" type="java.lang.InterruptedException">
 </exception>
@@ -280720,7 +280598,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 </method>
 <method name="offer"
@@ -280733,7 +280611,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 <parameter name="timeout" type="long">
 </parameter>
@@ -280789,7 +280667,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 </method>
 <method name="remainingCapacity"
@@ -281519,7 +281397,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="o" type="E">
+<parameter name="e" type="E">
 </parameter>
 </method>
 <method name="peek"
@@ -282224,7 +282102,7 @@
  extends="java.lang.Enum"
  abstract="false"
  static="false"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -282238,9 +282116,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="duration" type="long">
+<parameter name="sourceDuration" type="long">
 </parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
+<parameter name="sourceUnit" type="java.util.concurrent.TimeUnit">
 </parameter>
 </method>
 <method name="sleep"
@@ -284276,12 +284154,55 @@
 </package>
 <package name="java.util.concurrent.locks"
 >
-<class name="AbstractQueuedSynchronizer"
+<class name="AbstractOwnableSynchronizer"
  extends="java.lang.Object"
  abstract="true"
  static="false"
  final="false"
  deprecated="not deprecated"
+ visibility=""
+>
+<implements name="java.io.Serializable">
+</implements>
+<constructor name="AbstractOwnableSynchronizer"
+ type="java.util.concurrent.locks.AbstractOwnableSynchronizer"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</constructor>
+<method name="getExclusiveOwnerThread"
+ return="java.lang.Thread"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</method>
+<method name="setExclusiveOwnerThread"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="t" type="java.lang.Thread">
+</parameter>
+</method>
+</class>
+<class name="AbstractQueuedSynchronizer"
+ extends="java.util.concurrent.locks.AbstractOwnableSynchronizer"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
  visibility="public"
 >
 <implements name="java.io.Serializable">
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index e2d01de..f101007 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -202,7 +202,6 @@
     // draw and update only what we need
     mFlingerSurface->setSwapRectangle(updateRect);
 
-    glEnable(GL_SCISSOR_TEST);
     glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),
             updateRect.height());
 
@@ -218,6 +217,10 @@
         GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;
         GLint x = xc - offset;
 
+        glDisable(GL_SCISSOR_TEST);
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        glEnable(GL_SCISSOR_TEST);
         glDisable(GL_BLEND);
         glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
         glDrawTexiOES(x,                 yc, 0, mAndroid[1].w, mAndroid[1].h);
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index 346f156..3c82fe5 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -16,9 +16,12 @@
 
 #define LOG_TAG "BootAnimation"
 
+#include <cutils/properties.h>
+
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
+
 #include <utils/Log.h>
 #include <utils/threads.h>
 
@@ -41,12 +44,20 @@
     setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
 #endif
 
-    sp<ProcessState> proc(ProcessState::self());
-    ProcessState::self()->startThreadPool();
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.sf.nobootanimation", value, "0");
+    int noBootAnimation = atoi(value);
+    LOGI_IF(noBootAnimation,  "boot animation disabled");
+    if (!noBootAnimation) {
 
-    // create the boot animation object
-    sp<BootAnimation> boot = new BootAnimation();
+        sp<ProcessState> proc(ProcessState::self());
+        ProcessState::self()->startThreadPool();
 
-    IPCThreadState::self()->joinThreadPool();
+        // create the boot animation object
+        sp<BootAnimation> boot = new BootAnimation();
+
+        IPCThreadState::self()->joinThreadPool();
+
+    }
     return 0;
 }
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 12bdead..d8db8b3 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -119,7 +119,8 @@
     enc_meta->setInt32(kKeyWidth, width);
     enc_meta->setInt32(kKeyHeight, height);
 
-    OMXDecoder *encoder = OMXDecoder::CreateEncoder(&client, enc_meta);
+    OMXDecoder *encoder =
+        OMXDecoder::Create(&client, enc_meta, true /* createEncoder */);
 
     encoder->setSource(decoder);
     // encoder->setSource(meta, new DummySource(width, height));
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 961942a..7e23574 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -102,6 +102,7 @@
 int main(int argc, char **argv) {
     android::ProcessState::self()->startThreadPool();
 
+    bool audioOnly = false;
     if (argc > 1 && !strcmp(argv[1], "--list")) {
         sp<IServiceManager> sm = defaultServiceManager();
         sp<IBinder> binder = sm->getService(String16("media.player"));
@@ -121,6 +122,10 @@
         }
 
         return 0;
+    } else if (argc > 1 && !strcmp(argv[1], "--audio")) {
+        audioOnly = true;
+        ++argv;
+        --argc;
     }
 
 #if 0
@@ -149,7 +154,11 @@
         const char *mime;
         meta->findCString(kKeyMIMEType, &mime);
 
-        if (!strncasecmp(mime, "video/", 6)) {
+        if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
+            break;
+        }
+
+        if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
             break;
         }
     }
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index a3456c7..79bd6e7 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -163,6 +163,10 @@
         }
     }
 
+    /**
+     * Implement to return the implementation of the internal accessibility
+     * service interface.  Subclasses should not override.
+     */
     @Override
     public final IBinder onBind(Intent intent) {
         return new IEventListenerWrapper(this);
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index ba6cc32..3aeac53 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1085,6 +1085,14 @@
             reply.writeInt(result);
             return true;
         }
+        case KILL_APPLICATION_WITH_UID_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String pkg = data.readString();
+            int uid = data.readInt();
+            killApplicationWithUid(pkg, uid);
+            reply.writeNoException();
+            return true;
+        }
         }
         
         return super.onTransact(code, data, reply, flags);
@@ -2368,6 +2376,17 @@
         data.recycle();
         return result;
     }
+    public void killApplicationWithUid(String pkg, int uid) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(pkg);
+        data.writeInt(uid);
+        mRemote.transact(KILL_APPLICATION_WITH_UID_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
         
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e21b1d9..32a2891 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3634,7 +3634,7 @@
                 Locale.setDefault(config.locale);
             }
 
-            Resources.updateSystemConfiguration(config, null);
+            Resources.updateSystemConfiguration(config, dm);
 
             ApplicationContext.ApplicationPackageManager.configurationChanged();
             //Log.i(TAG, "Configuration changed in " + currentPackageName());
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 95b376c..b1b5282 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -266,6 +266,8 @@
             Intent intent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded)
             throws RemoteException;
+
+    public void killApplicationWithUid(String pkg, int uid) throws RemoteException;
         
     /*
      * Private non-Binder interfaces
@@ -421,4 +423,5 @@
     int REGISTER_ACTIVITY_WATCHER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+92;
     int UNREGISTER_ACTIVITY_WATCHER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+93;
     int START_ACTIVITY_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+94;
+    int KILL_APPLICATION_WITH_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+95;
 }
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index f9c38f9..f7479bc 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -273,11 +273,6 @@
         return null;
     }
 
-    private class IntentSenderWrapper extends IntentSender {
-        protected IntentSenderWrapper(IIntentSender target) {
-            super(target);
-        }
-    }
     /**
      * Retrieve a IntentSender object that wraps the existing sender of the PendingIntent
      *
@@ -285,7 +280,7 @@
      *
      */
     public IntentSender getIntentSender() {
-        return new IntentSenderWrapper(mTarget);
+        return new IntentSender(mTarget);
     }
 
     /**
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index dd70130..e991bc6 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -51,6 +51,7 @@
 import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.KeyEvent;
+import android.view.Menu;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -244,7 +245,12 @@
         }
         return success;
     }
-    
+
+    private boolean isInRealAppSearch() {
+        return !mGlobalSearchMode
+                && (mPreviousComponents == null || mPreviousComponents.isEmpty());
+    }
+
     /**
      * Called in response to a press of the hard search button in
      * {@link #onKeyDown(int, KeyEvent)}, this method toggles between in-app
@@ -535,7 +541,7 @@
         // we dismiss the entire dialog instead
         mSearchAutoComplete.setDropDownDismissedOnCompletion(false);
 
-        if (mGlobalSearchMode) {
+        if (!isInRealAppSearch()) {
             mSearchAutoComplete.setDropDownAlwaysVisible(true);  // fill space until results come in
         } else {
             mSearchAutoComplete.setDropDownAlwaysVisible(false);
@@ -662,7 +668,40 @@
         }
         mVoiceButton.setVisibility(visibility);
     }
-    
+
+    /*
+     * Menu.
+     */
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // Show search settings menu item if anyone handles the intent for it
+        Intent settingsIntent = new Intent(SearchManager.INTENT_ACTION_SEARCH_SETTINGS);
+        settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        PackageManager pm = getContext().getPackageManager();
+        ActivityInfo activityInfo = settingsIntent.resolveActivityInfo(pm, 0);
+        if (activityInfo != null) {
+            settingsIntent.setClassName(activityInfo.applicationInfo.packageName,
+                    activityInfo.name);
+            CharSequence label = activityInfo.loadLabel(getContext().getPackageManager());
+            menu.add(Menu.NONE, Menu.NONE, Menu.NONE, label)
+                    .setIcon(android.R.drawable.ic_menu_preferences)
+                    .setAlphabeticShortcut('P')
+                    .setIntent(settingsIntent);
+            return true;
+        }
+        return super.onCreateOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onMenuOpened(int featureId, Menu menu) {
+        // The menu shows up above the IME, regardless of whether it is in front
+        // of the drop-down or not. This looks weird when there is no IME, so
+        // we make sure it is visible.
+        mSearchAutoComplete.ensureImeVisible();
+        return super.onMenuOpened(featureId, menu);
+    }
+
     /**
      * Listeners of various types
      */
@@ -1259,10 +1298,13 @@
                 launchGlobalSearchIntent(intent);
             } else {
                 getContext().startActivity(intent);
-                // in global search mode, SearchDialogWrapper#performActivityResuming
+                // If the search switches to a different activity,
+                // SearchDialogWrapper#performActivityResuming
                 // will handle hiding the dialog when the next activity starts, but for
-                // in-app search, we still need to dismiss the dialog.
-                dismiss();
+                // real in-app search, we still need to dismiss the dialog.
+                if (isInRealAppSearch()) {
+                    dismiss();
+                }
             }
         } catch (RuntimeException ex) {
             Log.e(LOG_TAG, "Failed launch activity: " + intent, ex);
@@ -1401,13 +1443,13 @@
             return;
         }
         if (DBG) Log.d(LOG_TAG, "Switching to " + componentName);
-        
-        ComponentName previous = mLaunchComponent;
+
+        pushPreviousComponent(mLaunchComponent);
         if (!show(componentName, mAppSearchData, false)) {
             Log.w(LOG_TAG, "Failed to switch to source " + componentName);
+            popPreviousComponent();
             return;
         }
-        pushPreviousComponent(previous);
 
         String query = intent.getStringExtra(SearchManager.QUERY);
         setUserQuery(query);
@@ -1701,6 +1743,12 @@
                 if (mSearchDialog.backToPreviousComponent()) {
                     return true;
                 }
+                // If the drop-down obscures the keyboard, the user wouldn't see anything
+                // happening when pressing back, so we dismiss the entire dialog instead.
+                if (isInputMethodNotNeeded()) {
+                    mSearchDialog.cancel();
+                    return true;
+                }
                 return false; // will dismiss soft keyboard if necessary
             }
             return false;
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index d6044145..5782644 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -16,8 +16,6 @@
 
 package android.bluetooth;
 
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -75,11 +73,11 @@
 
     /** There was an error trying to obtain the state */
     public static final int STATE_ERROR        = -1;
-    /** No headset currently connected */
+    /** No Pce currently connected */
     public static final int STATE_DISCONNECTED = 0;
     /** Connection attempt in progress */
     public static final int STATE_CONNECTING   = 1;
-    /** A headset is currently connected */
+    /** A Pce is currently connected */
     public static final int STATE_CONNECTED    = 2;
 
     public static final int RESULT_FAILURE = 0;
@@ -87,11 +85,9 @@
     /** Connection canceled before completion. */
     public static final int RESULT_CANCELED = 2;
 
-    public static final int PRIORITY_AUTO = 100;
-    public static final int PRIORITY_OFF = 0;
     /**
      * An interface for notifying Bluetooth PCE IPC clients when they have
-     * been connected to the BluetoothHeadset service.
+     * been connected to the BluetoothPbap service.
      */
     public interface ServiceListener {
         /**
@@ -113,7 +109,7 @@
     }
 
     /**
-     * Create a BluetoothHeadset proxy object.
+     * Create a BluetoothPbap proxy object.
      */
     public BluetoothPbap(Context context, ServiceListener l) {
         mContext = context;
@@ -133,7 +129,7 @@
 
     /**
      * Close the connection to the backing service.
-     * Other public functions of BluetoothHeadset will return default error
+     * Other public functions of BluetoothPbap will return default error
      * results once close() has been called. Multiple invocations of close()
      * are ok.
      */
@@ -145,9 +141,9 @@
     }
 
     /**
-     * Get the current state of the Bluetooth Headset service.
+     * Get the current state of the BluetoothPbap service.
      * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
-     *         object is currently not connected to the Headset service.
+     *         object is currently not connected to the Pbap service.
      */
     public int getState() {
         if (DBG) log("getState()");
@@ -159,13 +155,13 @@
             Log.w(TAG, "Proxy not attached to service");
             if (DBG) log(Log.getStackTraceString(new Throwable()));
         }
-        return BluetoothHeadset.STATE_ERROR;
+        return BluetoothPbap.STATE_ERROR;
     }
 
     /**
-     * Get the Bluetooth address of the current headset.
+     * Get the Bluetooth address of the current Pce.
      * @return The Bluetooth address, or null if not in connected or connecting
-     *         state, or if this proxy object is not connected to the Headset
+     *         state, or if this proxy object is not connected to the Pbap
      *         service.
      */
     public String getPceAddress() {
@@ -182,9 +178,9 @@
     }
 
     /**
-     * Returns true if the specified headset is connected (does not include
+     * Returns true if the specified Pcs is connected (does not include
      * connecting). Returns false if not connected, or if this proxy object
-     * if not currently connected to the headset service.
+     * if not currently connected to the Pbap service.
      */
     public boolean isConnected(String address) {
         if (DBG) log("isConnected(" + address + ")");
@@ -200,9 +196,9 @@
     }
 
     /**
-     * Disconnects the current headset. Currently this call blocks, it may soon
+     * Disconnects the current Pce. Currently this call blocks, it may soon
      * be made asynchornous. Returns false if this proxy object is
-     * not currently connected to the Headset service.
+     * not currently connected to the Pbap service.
      */
     public boolean disconnectPce() {
         if (DBG) log("disconnectPce()");
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index 8b0b6ab..c0db01a 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -26,10 +26,14 @@
 import java.util.HashMap;
 
 public class ContentProviderOperation implements Parcelable {
-    private final static int TYPE_INSERT = 1;
-    private final static int TYPE_UPDATE = 2;
-    private final static int TYPE_DELETE = 3;
-    private final static int TYPE_COUNT = 4;
+    /** @hide exposed for unit tests */
+    public final static int TYPE_INSERT = 1;
+    /** @hide exposed for unit tests */
+    public final static int TYPE_UPDATE = 2;
+    /** @hide exposed for unit tests */
+    public final static int TYPE_DELETE = 3;
+    /** @hide exposed for unit tests */
+    public final static int TYPE_COUNT = 4;
 
     private final int mType;
     private final Uri mUri;
@@ -65,7 +69,7 @@
         mSelectionArgs = source.readInt() != 0 ? source.readStringArray() : null;
         mExpectedCount = source.readInt() != 0 ? source.readInt() : null;
         mValuesBackReferences = source.readInt() != 0
-                
+
                 ? ContentValues.CREATOR.createFromParcel(source)
                 : null;
         mSelectionArgsBackReferences = source.readInt() != 0
@@ -167,6 +171,11 @@
         return mUri;
     }
 
+    /** @hide exposed for unit tests */
+    public int getType() {
+        return mType;
+    }
+
     public boolean isWriteOperation() {
         return mType == TYPE_DELETE || mType == TYPE_INSERT || mType == TYPE_UPDATE;
     }
@@ -492,7 +501,7 @@
             }
             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
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c6c9835..84449ef 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1293,7 +1293,8 @@
      * Use with {@link #getSystemService} to retrieve an
      * {@blink android.backup.IBackupManager IBackupManager} for communicating
      * with the backup mechanism.
-     *
+     * @hide
+     * 
      * @see #getSystemService
      */
     public static final String BACKUP_SERVICE = "backup";
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 45a082a..15612ce 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -135,6 +135,7 @@
         return mBase.getPackageCodePath();
     }
 
+    /** @hide */
     @Override
     public File getSharedPrefsFile(String name) {
         return mBase.getSharedPrefsFile(name);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 66b507d..64ee60e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2412,7 +2412,7 @@
     /**
      * Create an intent from a URI.  This URI may encode the action,
      * category, and other intent fields, if it was returned by
-     * {@link #toUri}..  If the Intent was not generate by toUri(), its data
+     * {@link #toUri}.  If the Intent was not generate by toUri(), its data
      * will be the entire URI and its action will be ACTION_VIEW.
      *
      * <p>The URI given here must not be relative -- that is, it must include
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 4da49d9..0e4d984 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -52,6 +52,9 @@
  * categories, and components, and same flags), it will receive a IntentSender
  * representing the same token if that is still valid.
  *
+ * <p>Instances of this class can not be made directly, but rather must be
+ * created from an existing {@link android.app.PendingIntent} with
+ * {@link android.app.PendingIntent#getIntentSender() PendingIntent.getIntentSender()}.
  */
 public class IntentSender implements Parcelable {
     private final IIntentSender mTarget;
@@ -245,11 +248,13 @@
         return b != null ? new IntentSender(b) : null;
     }
 
-    protected IntentSender(IIntentSender target) {
+    /** @hide */
+    public IntentSender(IIntentSender target) {
         mTarget = target;
     }
 
-    protected IntentSender(IBinder target) {
+    /** @hide */
+    public IntentSender(IBinder target) {
         mTarget = IIntentSender.Stub.asInterface(target);
     }
 }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 9ca11cd..0a42a6f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -131,8 +131,9 @@
     public static final int FLAG_UPDATED_SYSTEM_APP = 1<<7;
     
     /**
-     * Value for {@link #flags}: this is set of the application has set
-     * its android:targetSdkVersion to something >= the current SDK version.
+     * Value for {@link #flags}: this is set of the application has specified
+     * {@link android.R.styleable#AndroidManifestApplication_testOnly
+     * android:testOnly} to be true.
      */
     public static final int FLAG_TEST_ONLY = 1<<8;
 
diff --git a/core/java/android/gesture/GestureLibrary.java b/core/java/android/gesture/GestureLibrary.java
index a29c2c8..ec2e78c 100644
--- a/core/java/android/gesture/GestureLibrary.java
+++ b/core/java/android/gesture/GestureLibrary.java
@@ -35,6 +35,7 @@
         return false;
     }
 
+    /** @hide */
     public Learner getLearner() {
         return mStore.getLearner();
     }
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 5486920..188e7ff 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -26,6 +26,7 @@
     void userActivity(long when, boolean noChangeLights);
     void userActivityWithForce(long when, boolean noChangeLights, boolean force);
     void setPokeLock(int pokey, IBinder lock, String tag);
+    int getSupportedWakeLockFlags();
     void setStayOnSetting(int val);
     long getScreenOnTime();
     void preventScreenOn(boolean prevent);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index bfcf2fc..d5934102 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -114,12 +114,14 @@
     private static final int WAKE_BIT_SCREEN_DIM = 4;
     private static final int WAKE_BIT_SCREEN_BRIGHT = 8;
     private static final int WAKE_BIT_KEYBOARD_BRIGHT = 16;
+    private static final int WAKE_BIT_PROXIMITY_SCREEN_OFF = 32;
     
     private static final int LOCK_MASK = WAKE_BIT_CPU_STRONG
                                         | WAKE_BIT_CPU_WEAK
                                         | WAKE_BIT_SCREEN_DIM
                                         | WAKE_BIT_SCREEN_BRIGHT
-                                        | WAKE_BIT_KEYBOARD_BRIGHT;
+                                        | WAKE_BIT_KEYBOARD_BRIGHT
+                                        | WAKE_BIT_PROXIMITY_SCREEN_OFF;
 
     /**
      * Wake lock that ensures that the CPU is running.  The screen might
@@ -147,6 +149,16 @@
     public static final int SCREEN_DIM_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_DIM;
 
     /**
+     * Wake lock that turns the screen off when the proximity sensor activates.
+     * Since not all devices have proximity sensors, use
+     * {@link #getSupportedWakeLockFlags() getSupportedWakeLockFlags()} to determine if
+     * this wake lock mode is supported.
+     *
+     * {@hide}
+     */
+    public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = WAKE_BIT_PROXIMITY_SCREEN_OFF;
+
+    /**
      * Normally wake locks don't actually wake the device, they just cause
      * it to remain on once it's already on.  Think of the video player
      * app as the normal behavior.  Notifications that pop up and want
@@ -196,6 +208,7 @@
             case SCREEN_DIM_WAKE_LOCK:
             case SCREEN_BRIGHT_WAKE_LOCK:
             case FULL_WAKE_LOCK:
+            case PROXIMITY_SCREEN_OFF_WAKE_LOCK:
                 break;
             default:
                 throw new IllegalArgumentException();
@@ -365,7 +378,33 @@
         } catch (RemoteException e) {
         }
     }
-    
+
+   /**
+     * Returns the set of flags for {@link #newWakeLock(int, String) newWakeLock()}
+     * that are supported on the device.
+     * For example, to test to see if the {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK}
+     * is supported:
+     *
+     * {@samplecode
+     * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+     * int supportedFlags = pm.getSupportedWakeLockFlags();
+     *  boolean proximitySupported = ((supportedFlags & PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)
+     *                                  == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK);
+     * }
+     *
+     * @return the set of supported WakeLock flags.
+     *
+     * {@hide}
+     */
+    public int getSupportedWakeLockFlags()
+    {
+        try {
+            return mService.getSupportedWakeLockFlags();
+        } catch (RemoteException e) {
+            return 0;
+        }
+    }
+
     private PowerManager()
     {
     }
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index abdcd93..db25cfa 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -28,6 +28,7 @@
 import android.provider.Settings;
 import android.provider.Settings.System;
 import android.util.AttributeSet;
+import android.view.KeyEvent;
 import android.view.View;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
@@ -36,7 +37,7 @@
  * @hide
  */
 public class VolumePreference extends SeekBarPreference implements 
-        PreferenceManager.OnActivityStopListener {
+        PreferenceManager.OnActivityStopListener, View.OnKeyListener {
 
     private static final String TAG = "VolumePreference";
     
@@ -66,6 +67,30 @@
         mSeekBarVolumizer = new SeekBarVolumizer(getContext(), seekBar, mStreamType);
         
         getPreferenceManager().registerOnActivityStopListener(this);
+
+        // grab focus and key events so that pressing the volume buttons in the
+        // dialog doesn't also show the normal volume adjust toast.
+        view.setOnKeyListener(this);
+        view.setFocusableInTouchMode(true);
+        view.requestFocus();
+    }
+
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        boolean isdown = (event.getAction() == KeyEvent.ACTION_DOWN);
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_VOLUME_DOWN:
+                if (isdown) {
+                    mSeekBarVolumizer.changeVolumeBy(-1);
+                }
+                return true;
+            case KeyEvent.KEYCODE_VOLUME_UP:
+                if (isdown) {
+                    mSeekBarVolumizer.changeVolumeBy(1);
+                }
+                return true;
+            default:
+                return false;
+        }
     }
 
     @Override
@@ -158,7 +183,9 @@
             }
 
             mRingtone = RingtoneManager.getRingtone(mContext, defaultUri);
-            mRingtone.setStreamType(mStreamType);
+            if (mRingtone != null) {
+                mRingtone.setStreamType(mStreamType);
+            }
         }
         
         public void stop() {
@@ -215,5 +242,12 @@
             return mSeekBar;
         }
         
+        public void changeVolumeBy(int amount) {
+            mSeekBar.incrementProgressBy(amount);
+            if (mRingtone != null && !mRingtone.isPlaying()) {
+                sample();
+            }
+            postSetVolume(mSeekBar.getProgress());
+        }
     }
 }
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index fce8630..01189fe 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -113,7 +113,7 @@
         public static final String SEND_TO_VOICEMAIL = "send_to_voicemail";
     }
 
-    private interface AggregatesColumns {
+    private interface ContactsColumns {
         /**
          * The display name for the contact.
          * <P>Type: TEXT</P>
@@ -140,7 +140,7 @@
 
         /**
          * Lookup value that reflects the {@link Groups#GROUP_VISIBLE} state of
-         * any {@link GroupMembership} for this aggregate.
+         * any {@link GroupMembership} for this contact.
          */
         public static final String IN_VISIBLE_GROUP = "in_visible_group";
 
@@ -171,32 +171,33 @@
 
 
     /**
-     * Constants for the aggregates table, which contains a record per group
-     * of contact representing the same person.
+     * Constants for the contacts table, which contains a record per group
+     * of raw contact representing the same person.
      */
-    public static final class Aggregates implements BaseColumns, AggregatesColumns,
+    // TODO make final once renaming is complete
+    public static /*final*/ class Contacts implements BaseColumns, ContactsColumns,
             ContactOptionsColumns {
         /**
          * This utility class cannot be instantiated
          */
-        private Aggregates()  {}
+        private Contacts()  {}
 
         /**
          * The content:// style URI for this table
          */
-        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "aggregates");
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "contacts");
 
         /**
          * The content:// style URI for this table joined with useful data from
          * {@link Data}.
          */
         public static final Uri CONTENT_SUMMARY_URI = Uri.withAppendedPath(AUTHORITY_URI,
-                "aggregates_summary");
+                "contacts_summary");
 
         /**
          * The content:// style URI used for "type-to-filter" functionality on the
          * {@link #CONTENT_SUMMARY_URI} URI. The filter string will be used to match
-         * various parts of the aggregate name. The filter argument should be passed
+         * various parts of the contact name. The filter argument should be passed
          * as an additional path segment after this URI.
          */
         public static final Uri CONTENT_SUMMARY_FILTER_URI = Uri.withAppendedPath(
@@ -204,8 +205,8 @@
 
         /**
          * The content:// style URI for this table joined with useful data from
-         * {@link Data}, filtered to include only starred aggregates
-         * and the most frequently contacted aggregates.
+         * {@link Data}, filtered to include only starred contacts
+         * and the most frequently contacted contacts.
          */
         public static final Uri CONTENT_SUMMARY_STREQUENT_URI = Uri.withAppendedPath(
                 CONTENT_SUMMARY_URI, "strequent");
@@ -213,7 +214,7 @@
         /**
          * The content:// style URI used for "type-to-filter" functionality on the
          * {@link #CONTENT_SUMMARY_STREQUENT_URI} URI. The filter string will be used to match
-         * various parts of the aggregate name. The filter argument should be passed
+         * various parts of the contact name. The filter argument should be passed
          * as an additional path segment after this URI.
          */
         public static final Uri CONTENT_SUMMARY_STREQUENT_FILTER_URI = Uri.withAppendedPath(
@@ -225,16 +226,16 @@
          * The MIME type of {@link #CONTENT_URI} providing a directory of
          * people.
          */
-        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person_aggregate";
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact";
 
         /**
          * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
          * person.
          */
-        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/person_aggregate";
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact";
 
         /**
-         * A sub-directory of a single contact aggregate that contains all of their
+         * A sub-directory of a single contact that contains all of the constituent raw contact
          * {@link Data} rows.
          */
         public static final class Data implements BaseColumns, DataColumns {
@@ -251,10 +252,10 @@
 
         /**
          * A sub-directory of a single contact aggregate that contains all aggregation suggestions
-         * (other aggregates).  The aggregation suggestions are computed based on approximate
-         * data matches with this aggregate.
+         * (other contacts).  The aggregation suggestions are computed based on approximate
+         * data matches with this contact.
          */
-        public static final class AggregationSuggestions implements BaseColumns, AggregatesColumns {
+        public static final class AggregationSuggestions implements BaseColumns, ContactsColumns {
             /**
              * No public constructor since this is a utility class
              */
@@ -275,6 +276,9 @@
         }
     }
 
+    @Deprecated
+    public static final class Aggregates extends Contacts {}
+
     /**
      * Columns that appear when each row of a table belongs to a specific
      * account, including sync information that an account may need.
@@ -317,10 +321,11 @@
 
     private interface RawContactsColumns {
         /**
-         * A reference to the {@link Aggregates#_ID} that this data belongs to.
+         * A reference to the {@link android.provider.ContactsContract.Contacts#_ID} that this
+         * data belongs to.
          * <P>Type: INTEGER</P>
          */
-        public static final String AGGREGATE_ID = "aggregate_id";
+        public static final String CONTACT_ID = "contact_id";
 
         /**
          * Flag indicating that this {@link RawContacts} entry and its children has
@@ -340,11 +345,10 @@
         /**
          * The "deleted" flag: "0" by default, "1" if the row has been marked
          * for deletion. When {@link android.content.ContentResolver#delete} is
-         * called on a contact, it is marked for deletion and removed from its
-         * aggregate. The sync adaptor deletes the contact on the server and
+         * called on a raw contact, it is marked for deletion and removed from its
+         * aggregate contact. The sync adaptor deletes the raw contact on the server and
          * then calls ContactResolver.delete once more, this time passing the
-         * {@link android.provider.ContactsContract.Contacts#DELETE_PERMANENTLY}
-         * query parameter to finalize the data removal.
+         * {@link RawContacts#DELETE_PERMANENTLY} query parameter to finalize the data removal.
          * <P>Type: INTEGER</P>
          */
         public static final String DELETED = "deleted";
@@ -355,8 +359,7 @@
      * information per sync source. Sync adapters and contact management apps
      * are the primary consumers of this API.
      */
-    // TODO make final as soon as renaming is complete
-    public static /*final*/ class RawContacts implements BaseColumns, RawContactsColumns,
+    public static final class RawContacts implements BaseColumns, RawContactsColumns,
             SyncColumns, ContactOptionsColumns {
         /**
          * This utility class cannot be instantiated
@@ -367,7 +370,7 @@
         /**
          * The content:// style URI for this table
          */
-        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "contacts");
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "raw_contacts");
 
         /**
          * The content:// style URL for filtering people by email address. The
@@ -383,13 +386,13 @@
          * The MIME type of {@link #CONTENT_URI} providing a directory of
          * people.
          */
-        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person";
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/raw_contact";
 
         /**
          * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
          * person.
          */
-        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/person";
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/raw_contact";
 
         /**
          * Query parameter that can be passed with the {@link #CONTENT_URI} URI
@@ -405,19 +408,19 @@
         public static final int AGGREGATION_MODE_DEFAULT = 0;
 
         /**
-         * Aggregation mode: aggregate at the time the contact is inserted/updated.
+         * Aggregation mode: aggregate at the time the raw contact is inserted/updated.
          */
         public static final int AGGREGATION_MODE_IMMEDITATE = 1;
 
         /**
-         * Aggregation mode: never aggregate this contact (note that the contact will not
+         * Aggregation mode: never aggregate this raw contact (note that the raw contact will not
          * have a corresponding Aggregate and therefore will not be included in Aggregates
          * query results.)
          */
         public static final int AGGREGATION_MODE_DISABLED = 2;
 
         /**
-         * A sub-directory of a single contact that contains all of their {@link Data} rows.
+         * A sub-directory of a single raw contact that contains all of their {@link Data} rows.
          * To access this directory append
          */
         public static final class Data implements BaseColumns, DataColumns {
@@ -434,13 +437,6 @@
         }
     }
 
-    /**
-     * TODO remove as soon as full renaming is complete.
-     */
-    @Deprecated
-    public static final class Contacts extends RawContacts {
-    }
-
     private interface DataColumns {
         /**
          * The package name to use when creating {@link Resources} objects for
@@ -455,20 +451,21 @@
         public static final String MIMETYPE = "mimetype";
 
         /**
-         * A reference to the {@link android.provider.ContactsContract.Contacts#_ID}
+         * A reference to the {@link RawContacts#_ID}
          * that this data belongs to.
          */
-        public static final String CONTACT_ID = "contact_id";
+        public static final String RAW_CONTACT_ID = "raw_contact_id";
 
         /**
-         * Whether this is the primary entry of its kind for the contact it belongs to
+         * Whether this is the primary entry of its kind for the raw 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".
+         * Whether this is the primary entry of its kind for the aggregate
+         * contact 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";
@@ -514,7 +511,7 @@
     }
 
     /**
-     * Constants for the data table, which contains data points tied to a contact.
+     * Constants for the data table, which contains data points tied to a raw contact.
      * For example, a phone number or email address. Each row in this table contains a type
      * definition and some generic columns. Each data type can define the meaning for each of
      * the generic columns.
@@ -539,10 +536,10 @@
     /**
      * A table that represents the result of looking up a phone number, for
      * example for caller ID. The table joins that data row for the phone number
-     * with the contact that owns the number. To perform a lookup you must
+     * with the raw contact that owns the number. To perform a lookup you must
      * append the number you want to find to {@link #CONTENT_FILTER_URI}.
      */
-    public static final class PhoneLookup implements BaseColumns, DataColumns, AggregatesColumns {
+    public static final class PhoneLookup implements BaseColumns, DataColumns, ContactsColumns {
         /**
          * This utility class cannot be instantiated
          */
@@ -562,7 +559,7 @@
 
     /**
      * Additional data mixed in with {@link Im.CommonPresenceColumns} to link
-     * back to specific {@link ContactsContract.Aggregates#_ID} entries.
+     * back to specific {@link ContactsContract.Contacts#_ID} entries.
      */
     private interface PresenceColumns {
 
@@ -573,11 +570,10 @@
         public static final String _ID = "presence_id";
 
         /**
-         * Reference to the {@link android.provider.ContactsContract.Contacts#_ID} this presence
-         * references.
+         * Reference to the {@link RawContacts#_ID} this presence references.
          * <P>Type: INTEGER</P>
          */
-        public static final String CONTACT_ID = "contact_id";
+        public static final String RAW_CONTACT_ID = "raw_contact_id";
 
         /**
          * Reference to the {@link Data#_ID} entry that owns this presence.
@@ -694,9 +690,12 @@
             public static final String MIMETYPE = "mimetype";
 
             /**
-             * The {@link Contacts#_ID} that this data belongs to.
+             * The {@link RawContacts#_ID} that this data belongs to.
              */
-            public static final String CONTACT_ID = "contact_id";
+            public static final String RAW_CONTACT_ID = "raw_contact_id";
+
+            @Deprecated
+            public static final String CONTACT_ID = RAW_CONTACT_ID;
         }
 
         /**
@@ -835,7 +834,7 @@
             /**
              * The content:// style URI for all data records of the
              * {@link Phone#CONTENT_ITEM_TYPE} MIME type, combined with the
-             * associated contact and aggregate data.
+             * associated raw contact and aggregate contact data.
              */
             public static final Uri CONTENT_URI = Uri.withAppendedPath(Data.CONTENT_URI,
                     "phones");
@@ -843,7 +842,7 @@
             /**
              * The content:// style URI for filtering data records of the
              * {@link Phone#CONTENT_ITEM_TYPE} MIME type, combined with the
-             * associated contact and aggregate data. The filter argument should
+             * associated raw contact and aggregate contact data. The filter argument should
              * be passed as an additional path segment after this URI.
              */
             public static final Uri CONTENT_FILTER_URI = Uri.withAppendedPath(CONTENT_URI,
@@ -1090,7 +1089,7 @@
             public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/photo";
 
             /**
-             * Thumbnail photo of the contact. This is the raw bytes of an image
+             * Thumbnail photo of the raw contact. This is the raw bytes of an image
              * that could be inflated using {@link BitmapFactory}.
              * <p>
              * Type: BLOB
@@ -1193,7 +1192,7 @@
         public static final String SYSTEM_ID = "system_id";
 
         /**
-         * The total number of {@link Aggregates} that have
+         * The total number of {@link Contacts} that have
          * {@link GroupMembership} in this group. Read-only value that is only
          * present when querying {@link Groups#CONTENT_SUMMARY_URI}.
          * <p>
@@ -1202,7 +1201,7 @@
         public static final String SUMMARY_COUNT = "summ_count";
 
         /**
-         * The total number of {@link Aggregates} that have both
+         * The total number of {@link Contacts} that have both
          * {@link GroupMembership} in this group, and also have phone numbers.
          * Read-only value that is only present when querying
          * {@link Groups#CONTENT_SUMMARY_URI}.
@@ -1290,31 +1289,33 @@
         public static final String TYPE = "type";
 
         /**
-         * Allows the provider to automatically decide whether the aggregate should include
-         * a particular contact or not.
+         * Allows the provider to automatically decide whether the aggregate
+         * contact should include a particular raw contact or not.
          */
         public static final int TYPE_AUTOMATIC = 0;
 
         /**
-         * Makes sure that the specified contact is included in the specified aggregate.
+         * Makes sure that the specified raw contact is included in the
+         * specified aggregate contact.
          */
         public static final int TYPE_KEEP_IN = 1;
 
         /**
-         * Makes sure that the specified contact is NOT included in the specified aggregate.
+         * Makes sure that the specified raw contact is NOT included in the
+         * specified aggregate contact.
          */
         public static final int TYPE_KEEP_OUT = 2;
 
         /**
-         * A reference to the {@link Aggregates#_ID} of the aggregate that the rule applies to.
-         */
-        public static final String AGGREGATE_ID = "aggregate_id";
-
-        /**
          * A reference to the {@link android.provider.ContactsContract.Contacts#_ID} of the
-         * contact that the rule applies to.
+         * aggregate contact that the rule applies to.
          */
         public static final String CONTACT_ID = "contact_id";
+
+        /**
+         * A reference to the {@link RawContacts#_ID} of the raw contact that the rule applies to.
+         */
+        public static final String RAW_CONTACT_ID = "raw_contact_id";
     }
 
     /**
diff --git a/core/java/android/provider/SocialContract.java b/core/java/android/provider/SocialContract.java
index a3b1d6e..d542774 100644
--- a/core/java/android/provider/SocialContract.java
+++ b/core/java/android/provider/SocialContract.java
@@ -19,7 +19,7 @@
 import android.content.res.Resources;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
-import android.provider.ContactsContract.Aggregates;
+import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 
 /**
@@ -165,7 +165,7 @@
 
         /**
          * The {@link Uri} for the latest social activity performed by any
-         * contact aggregated under the specified {@link Aggregates#_ID}. Will
+         * contact aggregated under the specified {@link Contacts#_ID}. Will
          * also join with most-present {@link Presence} for this aggregate.
          */
         public static final Uri CONTENT_AGGREGATE_STATUS_URI =
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index 0f0be79..81dd96e 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -124,10 +124,6 @@
                         "A List must have fewer than "
                         + Byte.MAX_VALUE + " items in it.");
             }
-            if (items.length < 1) {
-                throw new IllegalArgumentException(
-                        "A List must have at least one item in it.");
-            }
             for (int i = 0; i < items.length; i++) {
                 final Object item = items[i];
                 if (item == null) {
@@ -223,7 +219,7 @@
             case LIST:
                 if (mBuffer.remaining() < 1) return null;
                 int length = mBuffer.get();
-                if (length <= 0) return null;
+                if (length < 0) return null;
                 Object[] array = new Object[length];
                 for (int i = 0; i < length; ++i) {
                     array[i] = decodeObject();
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 9178131..c4bf642 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -107,8 +107,14 @@
     public static final int SURFACE_HIDDEN    = 0x01;
     
     /** Freeze the surface. Equivalent to calling freeze() */ 
+    public static final int SURFACE_FROZEN     = 0x02;
+
+    /**
+     * @deprecated use {@link #SURFACE_FROZEN} instead.
+     */
+    @Deprecated
     public static final int SURACE_FROZEN     = 0x02;
-    
+
     /** Enable dithering when compositing this surface */
     public static final int SURFACE_DITHER    = 0x04;
 
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 46aea02..4baf612 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.util.Config;
 import android.util.Log;
 import android.util.DisplayMetrics;
 import android.content.res.Resources;
@@ -140,7 +141,9 @@
     public static boolean consistencyCheckEnabled = false;
 
     static {
-        Debug.setFieldsOn(ViewDebug.class, true);
+        if (Config.DEBUG) {        
+	        Debug.setFieldsOn(ViewDebug.class, true);
+	    }
     }
 
     /**
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index a573983..e21824e 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -23,7 +23,9 @@
 import android.media.AudioManager;
 import android.media.AudioService;
 import android.media.AudioSystem;
+import android.media.RingtoneManager;
 import android.media.ToneGenerator;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Vibrator;
@@ -44,7 +46,7 @@
 public class VolumePanel extends Handler
 {
     private static final String TAG = "VolumePanel";
-    private static boolean LOGD = false || Config.LOGD;
+    private static boolean LOGD = false;
 
     /**
      * The delay before playing a sound. This small period exists so the user
@@ -86,6 +88,7 @@
     protected Context mContext;
     private AudioManager mAudioManager;
     protected AudioService mAudioService;
+    private boolean mRingIsSilent;
 
     private final Toast mToast;
     private final View mView;
@@ -138,7 +141,7 @@
             onShowVolumeChanged(streamType, flags);
         }
 
-        if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
+        if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
             removeMessages(MSG_PLAY_SOUND);
             sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
         }
@@ -157,6 +160,7 @@
         int index = mAudioService.getStreamVolume(streamType);
         int message = UNKNOWN_VOLUME_TEXT;
         int additionalMessage = 0;
+        mRingIsSilent = false;
 
         if (LOGD) {
             Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType
@@ -169,8 +173,15 @@
         switch (streamType) {
 
             case AudioManager.STREAM_RING: {
+                setRingerIcon();
                 message = RINGTONE_VOLUME_TEXT;
-                setRingerIcon(index);
+                Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
+                        mContext, RingtoneManager.TYPE_RINGTONE);
+                if (ringuri == null) {
+                    additionalMessage =
+                        com.android.internal.R.string.volume_music_hint_silent_ringtone_selected;
+                    mRingIsSilent = true;
+                }
                 break;
             }
 
@@ -208,6 +219,13 @@
             case AudioManager.STREAM_NOTIFICATION: {
                 message = NOTIFICATION_VOLUME_TEXT;
                 setSmallIcon(index);
+                Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
+                        mContext, RingtoneManager.TYPE_NOTIFICATION);
+                if (ringuri == null) {
+                    additionalMessage =
+                        com.android.internal.R.string.volume_music_hint_silent_ringtone_selected;
+                    mRingIsSilent = true;
+                }
                 break;
             }
 
@@ -254,7 +272,6 @@
                 mAudioService.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) {
             sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
         }
-
     }
 
     protected void onPlaySound(int streamType, int flags) {
@@ -337,17 +354,15 @@
     /**
      * Makes the ringer icon visible with an icon that is chosen
      * based on the current ringer mode.
-     *
-     * @param index
      */
-    private void setRingerIcon(int index) {
+    private void setRingerIcon() {
         mSmallStreamIcon.setVisibility(View.GONE);
         mLargeStreamIcon.setVisibility(View.VISIBLE);
 
         int ringerMode = mAudioService.getRingerMode();
         int icon;
 
-        if (LOGD) Log.d(TAG, "setRingerIcon(index: " + index+ "), ringerMode: " + ringerMode);
+        if (LOGD) Log.d(TAG, "setRingerIcon(), ringerMode: " + ringerMode);
 
         if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
             icon = com.android.internal.R.drawable.ic_volume_off;
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 3b67fb6..ed77ce8 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -72,38 +72,41 @@
     private final Context mContext;
 
     // Message Ids
-    private static final int PAGE_STARTED              = 100;
-    private static final int RECEIVED_ICON             = 101;
-    private static final int RECEIVED_TITLE            = 102;
-    private static final int OVERRIDE_URL              = 103;
-    private static final int AUTH_REQUEST              = 104;
-    private static final int SSL_ERROR                 = 105;
-    private static final int PROGRESS                  = 106;
-    private static final int UPDATE_VISITED            = 107;
-    private static final int LOAD_RESOURCE             = 108;
-    private static final int CREATE_WINDOW             = 109;
-    private static final int CLOSE_WINDOW              = 110;
-    private static final int SAVE_PASSWORD             = 111;
-    private static final int JS_ALERT                  = 112;
-    private static final int JS_CONFIRM                = 113;
-    private static final int JS_PROMPT                 = 114;
-    private static final int JS_UNLOAD                 = 115;
-    private static final int ASYNC_KEYEVENTS           = 116;
-    private static final int TOO_MANY_REDIRECTS        = 117;
-    private static final int DOWNLOAD_FILE             = 118;
-    private static final int REPORT_ERROR              = 119;
-    private static final int RESEND_POST_DATA          = 120;
-    private static final int PAGE_FINISHED             = 121;
-    private static final int REQUEST_FOCUS             = 122;
-    private static final int SCALE_CHANGED             = 123;
-    private static final int RECEIVED_CERTIFICATE      = 124;
-    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;
+    private static final int PAGE_STARTED                        = 100;
+    private static final int RECEIVED_ICON                       = 101;
+    private static final int RECEIVED_TITLE                      = 102;
+    private static final int OVERRIDE_URL                        = 103;
+    private static final int AUTH_REQUEST                        = 104;
+    private static final int SSL_ERROR                           = 105;
+    private static final int PROGRESS                            = 106;
+    private static final int UPDATE_VISITED                      = 107;
+    private static final int LOAD_RESOURCE                       = 108;
+    private static final int CREATE_WINDOW                       = 109;
+    private static final int CLOSE_WINDOW                        = 110;
+    private static final int SAVE_PASSWORD                       = 111;
+    private static final int JS_ALERT                            = 112;
+    private static final int JS_CONFIRM                          = 113;
+    private static final int JS_PROMPT                           = 114;
+    private static final int JS_UNLOAD                           = 115;
+    private static final int ASYNC_KEYEVENTS                     = 116;
+    private static final int TOO_MANY_REDIRECTS                  = 117;
+    private static final int DOWNLOAD_FILE                       = 118;
+    private static final int REPORT_ERROR                        = 119;
+    private static final int RESEND_POST_DATA                    = 120;
+    private static final int PAGE_FINISHED                       = 121;
+    private static final int REQUEST_FOCUS                       = 122;
+    private static final int SCALE_CHANGED                       = 123;
+    private static final int RECEIVED_CERTIFICATE                = 124;
+    private static final int SWITCH_OUT_HISTORY                  = 125;
+    private static final int EXCEEDED_DATABASE_QUOTA             = 126;
+    private static final int REACHED_APPCACHE_MAXSIZE            = 127;
+    private static final int JS_TIMEOUT                          = 128;
+    private static final int ADD_MESSAGE_TO_CONSOLE              = 129;
+    private static final int GEOLOCATION_PERMISSIONS_SHOW_PROMPT = 130;
+    private static final int GEOLOCATION_PERMISSIONS_HIDE_PROMPT = 131;
 
     // Message triggered by the client to resume execution
-    private static final int NOTIFY                    = 200;
+    private static final int NOTIFY                              = 200;
 
     // Result transportation object for returning results across thread
     // boundaries.
@@ -410,11 +413,49 @@
                     String url = (String) map.get("url");
                     long currentQuota =
                             ((Long) map.get("currentQuota")).longValue();
+                    long totalUsedQuota =
+                            ((Long) map.get("totalUsedQuota")).longValue();
                     WebStorage.QuotaUpdater quotaUpdater =
                         (WebStorage.QuotaUpdater) map.get("quotaUpdater");
 
                     mWebChromeClient.onExceededDatabaseQuota(url,
-                            databaseIdentifier, currentQuota, quotaUpdater);
+                            databaseIdentifier, currentQuota, totalUsedQuota,
+                            quotaUpdater);
+                }
+                break;
+
+            case REACHED_APPCACHE_MAXSIZE:
+                if (mWebChromeClient != null) {
+                    HashMap<String, Object> map =
+                            (HashMap<String, Object>) msg.obj;
+                    long spaceNeeded =
+                            ((Long) map.get("spaceNeeded")).longValue();
+                    long totalUsedQuota =
+                        ((Long) map.get("totalUsedQuota")).longValue();
+                    WebStorage.QuotaUpdater quotaUpdater =
+                        (WebStorage.QuotaUpdater) map.get("quotaUpdater");
+
+                    mWebChromeClient.onReachedMaxAppCacheSize(spaceNeeded,
+                            totalUsedQuota, quotaUpdater);
+                }
+                break;
+
+            case GEOLOCATION_PERMISSIONS_SHOW_PROMPT:
+                if (mWebChromeClient != null) {
+                    HashMap<String, Object> map =
+                            (HashMap<String, Object>) msg.obj;
+                    String origin = (String) map.get("origin");
+                    GeolocationPermissions.Callback callback =
+                            (GeolocationPermissions.Callback)
+                            map.get("callback");
+                    mWebChromeClient.onGeolocationPermissionsShowPrompt(origin,
+                            callback);
+                }
+                break;
+
+            case GEOLOCATION_PERMISSIONS_HIDE_PROMPT:
+                if (mWebChromeClient != null) {
+                    mWebChromeClient.onGeolocationPermissionsHidePrompt();
                 }
                 break;
 
@@ -1120,13 +1161,14 @@
      * @param databaseIdentifier The identifier of the database that the
      *     transaction that caused the overflow was running on.
      * @param currentQuota The current quota the origin is allowed.
+     * @param totalUsedQuota is the sum of all origins' quota.
      * @param quotaUpdater An instance of a class encapsulating a callback
      *     to WebViewCore to run when the decision to allow or deny more
      *     quota has been made.
      */
     public void onExceededDatabaseQuota(
             String url, String databaseIdentifier, long currentQuota,
-            WebStorage.QuotaUpdater quotaUpdater) {
+            long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
         if (mWebChromeClient == null) {
             quotaUpdater.updateQuota(currentQuota);
             return;
@@ -1137,12 +1179,78 @@
         map.put("databaseIdentifier", databaseIdentifier);
         map.put("url", url);
         map.put("currentQuota", currentQuota);
+        map.put("totalUsedQuota", totalUsedQuota);
         map.put("quotaUpdater", quotaUpdater);
         exceededQuota.obj = map;
         sendMessage(exceededQuota);
     }
 
     /**
+     * Called by WebViewCore to inform the Java side that the appcache has
+     * exceeded its max size.
+     * @param spaceNeeded is the amount of disk space that would be needed
+     * in order for the last appcache operation to succeed.
+     * @param totalUsedQuota is the sum of all origins' quota.
+     * @param quotaUpdater An instance of a class encapsulating a callback
+     * to WebViewCore to run when the decision to allow or deny a bigger
+     * app cache size has been made.
+     * @hide pending API council approval.
+     */
+    public void onReachedMaxAppCacheSize(long spaceNeeded,
+            long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
+        if (mWebChromeClient == null) {
+            quotaUpdater.updateQuota(0);
+            return;
+        }
+
+        Message msg = obtainMessage(REACHED_APPCACHE_MAXSIZE);
+        HashMap<String, Object> map = new HashMap();
+        map.put("spaceNeeded", spaceNeeded);
+        map.put("totalUsedQuota", totalUsedQuota);
+        map.put("quotaUpdater", quotaUpdater);
+        msg.obj = map;
+        sendMessage(msg);
+    }
+
+    /**
+     * Called by WebViewCore to instruct the browser to display a prompt to ask
+     * the user to set the Geolocation permission state for the given origin.
+     * @param origin The origin requesting Geolocation permsissions.
+     * @param callback The callback to call once a permission state has been
+     *     obtained.
+     * @hide pending API council review.
+     */
+    public void onGeolocationPermissionsShowPrompt(String origin,
+            GeolocationPermissions.Callback callback) {
+        if (mWebChromeClient == null) {
+            return;
+        }
+
+        Message showMessage =
+                obtainMessage(GEOLOCATION_PERMISSIONS_SHOW_PROMPT);
+        HashMap<String, Object> map = new HashMap();
+        map.put("origin", origin);
+        map.put("callback", callback);
+        showMessage.obj = map;
+        sendMessage(showMessage);
+    }
+
+    /**
+     * Called by WebViewCore to instruct the browser to hide the Geolocation
+     * permissions prompt.
+     * origin.
+     * @hide pending API council review.
+     */
+    public void onGeolocationPermissionsHidePrompt() {
+        if (mWebChromeClient == null) {
+            return;
+        }
+
+        Message hideMessage = obtainMessage(GEOLOCATION_PERMISSIONS_HIDE_PROMPT);
+        sendMessage(hideMessage);
+    }
+
+    /**
      * 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.
diff --git a/core/java/android/webkit/GeolocationPermissions.java b/core/java/android/webkit/GeolocationPermissions.java
new file mode 100755
index 0000000..d06d7e2
--- /dev/null
+++ b/core/java/android/webkit/GeolocationPermissions.java
@@ -0,0 +1,202 @@
+/*
+ * 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;
+
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.Set;
+
+
+/**
+ * Implements the Java side of GeolocationPermissions. Simply marshalls calls
+ * from the UI thread to the WebKit thread.
+ * @hide
+ */
+public final class GeolocationPermissions {
+    /**
+     * Callback interface used by the browser to report a Geolocation permission
+     * state set by the user in response to a permissions prompt.
+     */
+    public interface Callback {
+        public void invoke(String origin, boolean allow, boolean remember);
+    };
+
+    // Log tag
+    private static final String TAG = "geolocationPermissions";
+
+    // Global instance
+    private static GeolocationPermissions sInstance;
+
+    private Handler mHandler;
+
+    // Members used to transfer the origins and permissions between threads.
+    private Set<String> mOrigins;
+    private boolean mAllowed;
+    private static Lock mLock = new ReentrantLock();
+    private static boolean mUpdated;
+    private static Condition mUpdatedCondition = mLock.newCondition();
+
+    // Message ids
+    static final int GET_ORIGINS = 0;
+    static final int GET_ALLOWED = 1;
+    static final int CLEAR = 2;
+    static final int CLEAR_ALL = 3;
+
+    /**
+     * Gets the singleton instance of the class.
+     */
+    public static GeolocationPermissions getInstance() {
+      if (sInstance == null) {
+          sInstance = new GeolocationPermissions();
+      }
+      return sInstance;
+    }
+
+    /**
+     * Creates the message handler. Must be called on the WebKit thread.
+     */
+    public void createHandler() {
+        if (mHandler == null) {
+            mHandler = new Handler() {
+                @Override
+                public void handleMessage(Message msg) {
+                    // Runs on the WebKit thread.
+                    switch (msg.what) {
+                        case GET_ORIGINS:
+                            getOriginsImpl();
+                            break;
+                        case GET_ALLOWED:
+                            getAllowedImpl((String) msg.obj);
+                            break;
+                        case CLEAR:
+                            nativeClear((String) msg.obj);
+                            break;
+                        case CLEAR_ALL:
+                            nativeClearAll();
+                            break;
+                    }
+                }
+            };
+        }
+    }
+
+    /**
+     * Utility function to send a message to our handler.
+     */
+    private void postMessage(Message msg) {
+        assert(mHandler != null);
+        mHandler.sendMessage(msg);
+    }
+
+    /**
+     * Gets the set of origins for which Geolocation permissions are stored.
+     * Note that we represent the origins as strings. These are created using
+     * WebCore::SecurityOrigin::toString(). As long as all 'HTML 5 modules'
+     * (Database, Geolocation etc) do so, it's safe to match up origins for the
+     * purposes of displaying UI.
+     */
+    public Set getOrigins() {
+        // Called on the UI thread.
+        Set origins = null;
+        mLock.lock();
+        try {
+            mUpdated = false;
+            postMessage(Message.obtain(null, GET_ORIGINS));
+            while (!mUpdated) {
+                mUpdatedCondition.await();
+            }
+            origins = mOrigins;
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Exception while waiting for update", e);
+        } finally {
+            mLock.unlock();
+        }
+        return origins;
+    }
+
+    /**
+     * Helper method to get the set of origins.
+     */
+    private void getOriginsImpl() {
+        // Called on the WebKit thread.
+        mLock.lock();
+        mOrigins = nativeGetOrigins();
+        mUpdated = true;
+        mUpdatedCondition.signal();
+        mLock.unlock();
+    }
+
+    /**
+     * Gets the permission state for the specified origin.
+     */
+    public boolean getAllowed(String origin) {
+        // Called on the UI thread.
+        boolean allowed = false;
+        mLock.lock();
+        try {
+            mUpdated = false;
+            postMessage(Message.obtain(null, GET_ALLOWED, origin));
+            while (!mUpdated) {
+                mUpdatedCondition.await();
+            }
+            allowed = mAllowed;
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Exception while waiting for update", e);
+        } finally {
+            mLock.unlock();
+        }
+        return allowed;
+    }
+
+    /**
+     * Helper method to get the permission state.
+     */
+    private void getAllowedImpl(String origin) {
+        // Called on the WebKit thread.
+        mLock.lock();
+        mAllowed = nativeGetAllowed(origin);
+        mUpdated = true;
+        mUpdatedCondition.signal();
+        mLock.unlock();
+    }
+
+    /**
+     * Clears the permission state for the specified origin.
+     */
+    public void clear(String origin) {
+        // Called on the UI thread.
+        postMessage(Message.obtain(null, CLEAR, origin));
+    }
+
+    /**
+     * Clears the permission state for all origins.
+     */
+    public void clearAll() {
+        // Called on the UI thread.
+        postMessage(Message.obtain(null, CLEAR_ALL));
+    }
+
+    // Native functions, run on the WebKit thread.
+    private static native Set nativeGetOrigins();
+    private static native boolean nativeGetAllowed(String origin);
+    private static native void nativeClear(String origin);
+    private static native void nativeClearAll();
+}
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index dd43b8b..d52406d 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -198,17 +198,48 @@
     * @param databaseIdentifier The identifier of the database that caused the
     *     quota overflow.
     * @param currentQuota The current quota for the origin.
+    * @param totalUsedQuota is the sum of all origins' quota.
     * @param quotaUpdater A callback to inform the WebCore thread that a new
     *     quota is available. This callback must always be executed at some
     *     point to ensure that the sleeping WebCore thread is woken up.
     */
     public void onExceededDatabaseQuota(String url, String databaseIdentifier,
-        long currentQuota, WebStorage.QuotaUpdater quotaUpdater) {
+        long currentQuota, long totalUsedQuota,
+        WebStorage.QuotaUpdater quotaUpdater) {
         // This default implementation passes the current quota back to WebCore.
         // WebCore will interpret this that new quota was declined.
         quotaUpdater.updateQuota(currentQuota);
     }
 
+   /**
+    * Tell the client that the Application Cache has exceeded its max size.
+    * @param spaceNeeded is the amount of disk space that would be needed
+    * in order for the last appcache operation to succeed.
+    * @param totalUsedQuota is the sum of all origins' quota.
+    * @param quotaUpdater A callback to inform the WebCore thread that a new
+    * app cache size is available. This callback must always be executed at
+    * some point to ensure that the sleeping WebCore thread is woken up.
+    * @hide pending API council approval.
+    */
+    public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota,
+            WebStorage.QuotaUpdater quotaUpdater) {
+        quotaUpdater.updateQuota(0);
+    }
+
+    /**
+     * Instructs the client to show a prompt to ask the user to set the
+     * Geolocation permission state for the specified origin.
+     * @hide pending API council approval.
+     */
+    public void onGeolocationPermissionsShowPrompt(String origin,
+            GeolocationPermissions.Callback callback) {}
+
+    /**
+     * Instructs the client to hide the Geolocation permissions prompt.
+     * @hide pending API council approval.
+     */
+    public void onGeolocationPermissionsHidePrompt() {}
+
     /**
      * Tell the client that a JavaScript execution timeout has occured. And the
      * client may decide whether or not to interrupt the execution. If the
@@ -232,6 +263,5 @@
      * @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) {
-    }
+    public void addMessageToConsole(String message, int lineNumber, String sourceID) {}
 }
diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java
index 1a60dba..c3b359e 100644
--- a/core/java/android/webkit/WebStorage.java
+++ b/core/java/android/webkit/WebStorage.java
@@ -55,7 +55,8 @@
     // that we protect via a lock and update in syncValues().
     // This is needed to transfer this data across threads.
     private static Lock mLock = new ReentrantLock();
-    private static Condition mCacheUpdated = mLock.newCondition();
+    private static Condition mUpdateCondition = mLock.newCondition();
+    private static boolean mUpdateAvailable;
 
     // Message ids
     static final int UPDATE = 0;
@@ -133,8 +134,11 @@
         Set ret = null;
         mLock.lock();
         try {
+            mUpdateAvailable = false;
             update();
-            mCacheUpdated.await();
+            while (!mUpdateAvailable) {
+                mUpdateCondition.await();
+            }
             ret = mOrigins;
         } catch (InterruptedException e) {
             Log.e(TAG, "Exception while waiting on the updated origins", e);
@@ -155,8 +159,11 @@
         }
         mLock.lock();
         try {
+            mUpdateAvailable = false;
             update();
-            mCacheUpdated.await();
+            while (!mUpdateAvailable) {
+                mUpdateCondition.await();
+            }
             Long usage = mUsages.get(origin);
             if (usage != null) {
                 ret = usage.longValue();
@@ -180,8 +187,11 @@
         }
         mLock.lock();
         try {
+            mUpdateAvailable = false;
             update();
-            mCacheUpdated.await();
+            while (!mUpdateAvailable) {
+                mUpdateCondition.await();
+            }
             Long quota = mQuotas.get(origin);
             if (quota != null) {
                 ret = quota.longValue();
@@ -286,7 +296,8 @@
             mQuotas.put(origin, new Long(nativeGetQuotaForOrigin(origin)));
             mUsages.put(origin, new Long(nativeGetUsageForOrigin(origin)));
         }
-        mCacheUpdated.signal();
+        mUpdateAvailable = true;
+        mUpdateCondition.signal();
         mLock.unlock();
     }
 
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 19df87f..358fc9e 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -351,6 +351,9 @@
     VelocityTracker mVelocityTracker;
     private int mMaximumFling;
 
+    // use this flag to control whether enabling the new double tap zoom
+    static final boolean ENABLE_DOUBLETAP_ZOOM = true;
+
     /**
      * Touch mode
      */
@@ -370,6 +373,7 @@
     private static final int SCROLL_ZOOM_OUT = 11;
     private static final int LAST_SCROLL_ZOOM = 11;
     // end of touch mode values specific to scale+scroll
+    private static final int TOUCH_DOUBLE_TAP_MODE = 12;
 
     // Whether to forward the touch events to WebCore
     private boolean mForwardTouchEvents = false;
@@ -394,6 +398,8 @@
      */
     // pre-computed square of ViewConfiguration.getScaledTouchSlop()
     private int mTouchSlopSquare;
+    // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
+    private int mDoubleTapSlopSquare;
     // pre-computed density adjusted navigation slop
     private int mNavSlop;
     // This should be ViewConfiguration.getTapTimeout()
@@ -450,6 +456,7 @@
     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 RELEASE_SINGLE_TAP         = 5;
     private static final int REQUEST_FORM_DATA          = 6;
     private static final int SWITCH_TO_CLICK            = 7;
     private static final int RESUME_WEBCORE_UPDATE      = 8;
@@ -482,7 +489,7 @@
         "NEVER_REMEMBER_PASSWORD", //        = 2;
         "SWITCH_TO_SHORTPRESS", //           = 3;
         "SWITCH_TO_LONGPRESS", //            = 4;
-        "5",
+        "RELEASE_SINGLE_TAP", //             = 5;
         "REQUEST_FORM_DATA", //              = 6;
         "SWITCH_TO_CLICK", //                = 7;
         "RESUME_WEBCORE_UPDATE", //          = 8;
@@ -521,6 +528,16 @@
     // initial scale in percent. 0 means using default.
     private int mInitialScale = 0;
 
+    // while in the zoom overview mode, the page's width is fully fit to the
+    // current window. The page is alive, in another words, you can click to
+    // follow the links. Double tap will toggle between zoom overview mode and
+    // the last zoom scale.
+    boolean mInZoomOverview = false;
+    // ideally mZoomOverviewWidth should be mContentWidth. But sites like espn,
+    // engadget always have wider mContentWidth no matter what viewport size is.
+    int mZoomOverviewWidth = 0;
+    float mLastScale;
+
     // default scale. Depending on the display density.
     static int DEFAULT_SCALE_PERCENT;
     private float mDefaultScale;
@@ -762,9 +779,11 @@
         setLongClickable(true);
 
         final ViewConfiguration configuration = ViewConfiguration.get(getContext());
-        final int slop = configuration.getScaledTouchSlop();
+        int slop = configuration.getScaledTouchSlop();
         mTouchSlopSquare = slop * slop;
         mMinLockSnapReverseDistance = slop;
+        slop = configuration.getScaledDoubleTapSlop();
+        mDoubleTapSlopSquare = slop * slop;
         final float density = getContext().getResources().getDisplayMetrics().density;
         // use one line height, 16 based on our current default font, for how
         // far we allow a touch be away from the edge of a link
@@ -1124,6 +1143,9 @@
             b.putInt("scrollX", mScrollX);
             b.putInt("scrollY", mScrollY);
             b.putFloat("scale", mActualScale);
+            if (mInZoomOverview) {
+                b.putFloat("lastScale", mLastScale);
+            }
             return true;
         }
         return false;
@@ -1168,6 +1190,13 @@
                 // onSizeChanged() is called, the rest will be set
                 // correctly
                 mActualScale = scale;
+                float lastScale = b.getFloat("lastScale", -1.0f);
+                if (lastScale > 0) {
+                    mInZoomOverview = true;
+                    mLastScale = lastScale;
+                } else {
+                    mInZoomOverview = false;
+                }
                 invalidate();
                 return true;
             }
@@ -3060,6 +3089,11 @@
                 float y = mLastTouchY + (float) (mScrollY - lp.y);
                 mWebTextView.fakeTouchEvent(x, y);
             }
+            if (mInZoomOverview) {
+                // if in zoom overview mode, call doDoubleTap() to bring it back
+                // to normal mode so that user can enter text.
+                doDoubleTap();
+            }
         }
         else { // used by plugins
             imm.showSoftInput(this, 0);
@@ -3634,7 +3668,9 @@
         // update mMinZoomScale if the minimum zoom scale is not fixed
         if (!mMinZoomScaleFixed) {
             mMinZoomScale = (float) getViewWidth()
-                    / Math.max(ZOOM_OUT_WIDTH, mContentWidth);
+                    / Math.max(ZOOM_OUT_WIDTH, mDrawHistory ? mHistoryPicture
+                            .getWidth() : (mZoomOverviewWidth > 0 ?
+                                    mZoomOverviewWidth : mContentWidth));
         }
 
         // we always force, in case our height changed, in which case we still
@@ -3755,6 +3791,15 @@
                     nativeMoveSelection(viewToContent(mSelectX)
                             , viewToContent(mSelectY), false);
                     mTouchSelection = mExtendSelection = true;
+                } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
+                    mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
+                    if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
+                        mTouchMode = TOUCH_DOUBLE_TAP_MODE;
+                    } else {
+                        // commit the short press action for the previous tap
+                        doShortPress();
+                        // continue, mTouchMode should be still TOUCH_INIT_MODE
+                    }
                 } else {
                     mTouchMode = TOUCH_INIT_MODE;
                     mPreventDrag = mForwardTouchEvents;
@@ -3764,7 +3809,8 @@
                     }
                 }
                 // Trigger the link
-                if (mTouchMode == TOUCH_INIT_MODE) {
+                if (mTouchMode == TOUCH_INIT_MODE
+                        || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
                     mPrivateHandler.sendMessageDelayed(mPrivateHandler
                             .obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
                 }
@@ -3810,7 +3856,8 @@
                     if (mTouchMode == TOUCH_SHORTPRESS_MODE
                             || mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
                         mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                    } else if (mTouchMode == TOUCH_INIT_MODE) {
+                    } else if (mTouchMode == TOUCH_INIT_MODE
+                            || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
                         mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
                     }
 
@@ -3836,7 +3883,7 @@
                                 .sendMessage(EventHub.SET_SNAP_ANCHOR, 0, 0);
                     }
                     WebSettings settings = getSettings();
-                    if (settings.supportZoom()
+                    if (settings.supportZoom() && !mInZoomOverview
                             && settings.getBuiltInZoomControls()
                             && !mZoomButtonsController.isVisible()
                             && (canZoomScrollOut() ||
@@ -3905,7 +3952,7 @@
                     mUserScroll = true;
                 }
 
-                if (!getSettings().getBuiltInZoomControls()) {
+                if (!getSettings().getBuiltInZoomControls() && !mInZoomOverview) {
                     boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
                     boolean showMagnify = canZoomScrollOut();
                     if (mZoomControls != null && (showPlusMinus || showMagnify)) {
@@ -3929,7 +3976,22 @@
             case MotionEvent.ACTION_UP: {
                 mLastTouchUpTime = eventTime;
                 switch (mTouchMode) {
+                    case TOUCH_DOUBLE_TAP_MODE: // double tap
+                        mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
+                        doDoubleTap();
+                        break;
                     case TOUCH_INIT_MODE: // tap
+                        if (ENABLE_DOUBLETAP_ZOOM) {
+                            mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
+                            if (!mPreventDrag) {
+                                mPrivateHandler.sendMessageDelayed(
+                                        mPrivateHandler.obtainMessage(
+                                        RELEASE_SINGLE_TAP),
+                                        ViewConfiguration.getDoubleTapTimeout());
+                            }
+                            break;
+                        }
+                        // fall through
                     case TOUCH_SHORTPRESS_START_MODE:
                     case TOUCH_SHORTPRESS_MODE:
                         mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
@@ -4365,6 +4427,9 @@
             mInvInitialZoomScale = 1.0f / oldScale;
             mInvFinalZoomScale = 1.0f / mActualScale;
             mZoomScale = mActualScale;
+            if (!mInZoomOverview) {
+                mLastScale = scale;
+            }
             invalidate();
             return true;
         } else {
@@ -4470,6 +4535,9 @@
     public boolean zoomIn() {
         // TODO: alternatively we can disallow this during draw history mode
         switchOutDrawHistory();
+        // Center zooming to the center of the screen.
+        mZoomCenterX = getViewWidth() * .5f;
+        mZoomCenterY = getViewHeight() * .5f;
         return zoomWithPreview(mActualScale * 1.25f);
     }
 
@@ -4480,6 +4548,9 @@
     public boolean zoomOut() {
         // TODO: alternatively we can disallow this during draw history mode
         switchOutDrawHistory();
+        // Center zooming to the center of the screen.
+        mZoomCenterX = getViewWidth() * .5f;
+        mZoomCenterY = getViewHeight() * .5f;
         return zoomWithPreview(mActualScale * 0.8f);
     }
 
@@ -4571,6 +4642,50 @@
         }
     }
 
+    private void doDoubleTap() {
+        if (mWebViewCore.getSettings().getUseWideViewPort() == false) {
+            return;
+        }
+        mZoomCenterX = mLastTouchX;
+        mZoomCenterY = mLastTouchY;
+        mInZoomOverview = !mInZoomOverview;
+        if (mInZoomOverview) {
+            float newScale = (float) getViewWidth()
+                    / (mZoomOverviewWidth > 0 ? mZoomOverviewWidth
+                            : mContentWidth);
+            if (Math.abs(newScale - mActualScale) < 0.01) {
+                mInZoomOverview = !mInZoomOverview;
+                // as it is already full screen, do nothing.
+                return;
+            }
+            if (getSettings().getBuiltInZoomControls()) {
+                if (mZoomButtonsController.isVisible()) {
+                    mZoomButtonsController.setVisible(false);
+                }
+            } else {
+                if (mZoomControlRunnable != null) {
+                    mPrivateHandler.removeCallbacks(mZoomControlRunnable);
+                }
+                if (mZoomControls != null) {
+                    mZoomControls.hide();
+                }
+            }
+            zoomWithPreview(newScale);
+        } else {
+            // mLastTouchX and mLastTouchY are the point in the current viewport
+            int contentX = viewToContent((int) mLastTouchX + mScrollX);
+            int contentY = viewToContent((int) mLastTouchY + mScrollY);
+            int left = nativeGetBlockLeftEdge(contentX, contentY);
+            if (left != NO_LEFTEDGE) {
+                // add a 5pt padding to the left edge. Re-calculate the zoom
+                // center so that the new scroll x will be on the left edge.
+                mZoomCenterX = left < 5 ? 0 : (left - 5) * mLastScale
+                        * mActualScale / (mLastScale - mActualScale);
+            }
+            zoomWithPreview(mLastScale);
+        }
+    }
+
     // Called by JNI to handle a touch on a node representing an email address,
     // address, or phone number
     private void overrideLoading(String url) {
@@ -4791,6 +4906,8 @@
                     if (mTouchMode == TOUCH_INIT_MODE) {
                         mTouchMode = TOUCH_SHORTPRESS_START_MODE;
                         updateSelection();
+                    } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
+                        mTouchMode = TOUCH_DONE_MODE;
                     }
                     break;
                 }
@@ -4802,6 +4919,13 @@
                     }
                     break;
                 }
+                case RELEASE_SINGLE_TAP: {
+                    if (!mPreventDrag) {
+                        mTouchMode = TOUCH_DONE_MODE;
+                        doShortPress();
+                    }
+                    break;
+                }
                 case SWITCH_TO_CLICK:
                     // The user clicked with the trackball, and did not click a
                     // second time, so perform the action of a trackball single
@@ -4854,6 +4978,7 @@
                     break;
                 case NEW_PICTURE_MSG_ID:
                     // called for new content
+                    final int viewWidth = getViewWidth();
                     final WebViewCore.DrawData draw =
                             (WebViewCore.DrawData) msg.obj;
                     final Point viewSize = draw.mViewPoint;
@@ -4861,16 +4986,12 @@
                         // use the same logic in sendViewSizeZoom() to make sure
                         // the mZoomScale has matched the viewSize so that we
                         // can clear mZoomScale
-                        if (Math.round(getViewWidth() / mZoomScale) == viewSize.x) {
+                        if (Math.round(viewWidth / mZoomScale) == viewSize.x) {
                             mZoomScale = 0;
                             mWebViewCore.sendMessage(EventHub.SET_SNAP_ANCHOR,
                                     0, 0);
                         }
                     }
-                    if (!mMinZoomScaleFixed) {
-                        mMinZoomScale = (float) getViewWidth()
-                                / Math.max(ZOOM_OUT_WIDTH, draw.mWidthHeight.x);
-                    }
                     // We update the layout (i.e. request a layout from the
                     // view system) if the last view size that we sent to
                     // WebCore matches the view size of the picture we just
@@ -4888,6 +5009,25 @@
                     if (mPictureListener != null) {
                         mPictureListener.onNewPicture(WebView.this, capturePicture());
                     }
+                    if (mWebViewCore.getSettings().getUseWideViewPort()) {
+                        mZoomOverviewWidth = Math.max(draw.mMinPrefWidth,
+                                draw.mViewPoint.x);
+                    }
+                    if (!mMinZoomScaleFixed) {
+                        mMinZoomScale = (float) viewWidth
+                                / Math.max(ZOOM_OUT_WIDTH,
+                                mZoomOverviewWidth > 0 ? mZoomOverviewWidth
+                                        : mContentWidth);
+                    }
+                    if (!mDrawHistory && mInZoomOverview) {
+                        // fit the content width to the current view. Ignore
+                        // the rounding error case.
+                        if (Math.abs((viewWidth * mInvActualScale)
+                                - mZoomOverviewWidth) > 1) {
+                            zoomWithPreview((float) viewWidth
+                                    / mZoomOverviewWidth);
+                        }
+                    }
                     break;
                 case WEBCORE_INITIALIZED_MSG_ID:
                     // nativeCreate sets mNativeClass to a non-zero value
@@ -4916,7 +5056,7 @@
                         }
                     }
                     break;
-                case DID_FIRST_LAYOUT_MSG_ID:
+                case DID_FIRST_LAYOUT_MSG_ID: {
                     if (mNativeClass == 0) {
                         break;
                     }
@@ -4943,15 +5083,17 @@
                     if (width == 0) {
                         break;
                     }
+                    final WebSettings settings = mWebViewCore.getSettings();
                     int initialScale = msg.arg1;
                     int viewportWidth = msg.arg2;
                     // start a new page with DEFAULT_SCALE zoom scale.
                     float scale = mDefaultScale;
+                    mInZoomOverview = false;
                     if (mInitialScale > 0) {
                         scale = mInitialScale / 100.0f;
                     } else  {
-                        if (initialScale < 0) break;
-                        if (mWebViewCore.getSettings().getUseWideViewPort()) {
+                        if (initialScale == -1) break;
+                        if (settings.getUseWideViewPort()) {
                             // force viewSizeChanged by setting mLastWidthSent
                             // to 0
                             mLastWidthSent = 0;
@@ -4961,11 +5103,21 @@
                             // than the view width, zoom in to fill the view
                             if (viewportWidth > 0 && viewportWidth < width) {
                                 scale = (float) width / viewportWidth;
+                            } else {
+                                if (settings.getUseWideViewPort()) {
+                                    mInZoomOverview = ENABLE_DOUBLETAP_ZOOM;
+                                }
                             }
+                        } else if (initialScale < 0) {
+                            // this should only happen when
+                            // ENABLE_DOUBLETAP_ZOOM is true
+                            mInZoomOverview = true;
+                            scale = -initialScale / 100.0f;
                         } else {
                             scale = initialScale / 100.0f;
                         }
                     }
+                    mLastScale = scale;
                     setNewZoomScale(scale, false);
                     // As we are on a new page, remove the WebTextView.  This
                     // is necessary for page loads driven by webkit, and in
@@ -4973,6 +5125,7 @@
                     // the WebTextView was visible.
                     clearTextEntry();
                     break;
+                }
                 case MOVE_OUT_OF_PLUGIN:
                     if (nativePluginEatsNavKey()) {
                         navHandledKey(msg.arg1, 1, false, 0, true);
@@ -5542,4 +5695,7 @@
     private native void     nativeUpdateCachedTextfield(String updatedText,
             int generation);
     private native void     nativeUpdatePluginReceivesEvents();
+    // return NO_LEFTEDGE means failure.
+    private static final int NO_LEFTEDGE = -1;
+    private native int      nativeGetBlockLeftEdge(int x, int y);
 }
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 4993fcf..4afc4cd 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -36,6 +36,7 @@
 import android.view.SurfaceView;
 
 import java.util.ArrayList;
+import java.util.Set;
 
 import junit.framework.Assert;
 
@@ -96,7 +97,8 @@
 
     private boolean mViewportUserScalable = true;
 
-    private int mRestoredScale = WebView.DEFAULT_SCALE_PERCENT;
+    private int mRestoredScale = 0;
+    private int mRestoredScreenWidthScale = 0;
     private int mRestoredX = 0;
     private int mRestoredY = 0;
 
@@ -165,6 +167,8 @@
         WebIconDatabase.getInstance().createHandler();
         // Create the handler for WebStorage
         WebStorage.getInstance().createHandler();
+        // Create the handler for GeolocationPermissions.
+        GeolocationPermissions.getInstance().createHandler();
         // The transferMessages call will transfer all pending messages to the
         // WebCore thread handler.
         mEventHub.transferMessages();
@@ -246,7 +250,7 @@
     }
 
     /**
-     * Notify the user that the origin has exceeded it's database quota.
+     * Notify the browser that the origin has exceeded it's database quota.
      * @param url The URL that caused the overflow.
      * @param databaseIdentifier The identifier of the database.
      * @param currentQuota The current quota for the origin.
@@ -259,14 +263,55 @@
         // awaken the sleeping webcore thread when a decision from the
         // client to allow or deny quota is available.
         mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier,
-                currentQuota, new WebStorage.QuotaUpdater() {
+                currentQuota, getUsedQuota(), new WebStorage.QuotaUpdater() {
                                   public void updateQuota(long quota) {
-                                      nativeSetDatabaseQuota(quota);
+                                      nativeSetNewStorageLimit(quota);
                                   }
                               });
     }
 
     /**
+     * Notify the browser that the appcache has exceeded its max size.
+     * @param spaceNeeded is the amount of disk space that would be needed
+     * in order for the last appcache operation to succeed.
+     */
+    protected void reachedMaxAppCacheSize(long spaceNeeded) {
+        mCallbackProxy.onReachedMaxAppCacheSize(spaceNeeded, getUsedQuota(),
+                new WebStorage.QuotaUpdater() {
+                    public void updateQuota(long quota) {
+                        nativeSetNewStorageLimit(quota);
+                    }
+                });
+    }
+
+    /**
+     * Shows a prompt to ask the user to set the Geolocation permission state
+     * for the given origin.
+     * @param origin The origin for which Geolocation permissions are
+     *     requested.
+     */
+    protected void geolocationPermissionsShowPrompt(String origin) {
+        mCallbackProxy.onGeolocationPermissionsShowPrompt(origin,
+                new GeolocationPermissions.Callback() {
+          public void invoke(String origin, boolean allow, boolean remember) {
+            GeolocationPermissionsData data = new GeolocationPermissionsData();
+            data.mOrigin = origin;
+            data.mAllow = allow;
+            data.mRemember = remember;
+            // Marshall to WebCore thread.
+            sendMessage(EventHub.GEOLOCATION_PERMISSIONS_PROVIDE, data);
+          }
+        });
+    }
+
+    /**
+     * Hides the Geolocation permissions prompt.
+     */
+    protected void geolocationPermissionsHidePrompt() {
+        mCallbackProxy.onGeolocationPermissionsHidePrompt();
+    }
+
+    /**
      * Invoke a javascript confirm dialog.
      * @param message The message displayed in the dialog.
      * @return True if the user confirmed or false if the user cancelled.
@@ -439,14 +484,24 @@
 
     /*
      * Inform webcore that the user has decided whether to allow or deny new
-     * quota for the current origin and that the main thread should wake up
-     * now.
-     * @param quota The new quota.
+     * quota for the current origin or more space for the app cache, and that
+     * the main thread should wake up now.
+     * @param limit Is the new quota for an origin or new app cache max size.
      */
-    private native void nativeSetDatabaseQuota(long quota);
+    private native void nativeSetNewStorageLimit(long limit);
 
     private native void nativeUpdatePluginState(int framePtr, int nodePtr, int state);
 
+    /**
+     * Provide WebCore with a Geolocation permission state for the specified
+     * origin.
+     * @param origin The origin for which Geolocation permissions are provided.
+     * @param allow Whether Geolocation permissions are allowed.
+     * @param remember Whether this decision should be remembered beyond the
+     *     life of the current page.
+     */
+    private native void nativeGeolocationPermissionsProvide(String origin, boolean allow, boolean remember);
+
     // EventHub for processing messages
     private final EventHub mEventHub;
     // WebCore thread handler
@@ -590,6 +645,12 @@
         int mState;
     }
 
+    static class GeolocationPermissionsData {
+        String mOrigin;
+        boolean mAllow;
+        boolean mRemember;
+    }
+
         static final String[] HandlerDebugString = {
             "SCROLL_TEXT_INPUT", // = 99
             "LOAD_URL", // = 100;
@@ -715,6 +776,8 @@
         static final int DUMP_NAVTREE = 172;
 
         static final int SET_JS_FLAGS = 173;
+        // Geolocation
+        static final int GEOLOCATION_PERMISSIONS_PROVIDE = 180;
 
         // private message ids
         private static final int DESTROY =     200;
@@ -1101,6 +1164,12 @@
 
                         case SET_JS_FLAGS:
                             nativeSetJsFlags((String)msg.obj);
+
+                        case GEOLOCATION_PERMISSIONS_PROVIDE:
+                            GeolocationPermissionsData data =
+                                    (GeolocationPermissionsData) msg.obj;
+                            nativeGeolocationPermissionsProvide(data.mOrigin,
+                                    data.mAllow, data.mRemember);
                             break;
 
                         case SYNC_SCROLL:
@@ -1340,9 +1409,8 @@
             Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
             return;
         }
-        if (mSettings.getUseWideViewPort()
-                && (w < mViewportWidth || mViewportWidth == -1)) {
-            int width = mViewportWidth;
+        if (mSettings.getUseWideViewPort()) {
+            int width;
             if (mViewportWidth == -1) {
                 if (mSettings.getLayoutAlgorithm() ==
                         WebSettings.LayoutAlgorithm.NORMAL) {
@@ -1362,9 +1430,15 @@
                      */
                     width = Math.max(w, nativeGetContentMinPrefWidth());
                 }
+            } else {
+                width = Math.max(w, mViewportWidth);
             }
-            nativeSetSize(width, Math.round((float) width * h / w), w, scale,
-                    w, h);
+            // while in zoom overview mode, the text are wrapped to the screen
+            // width matching mWebView.mLastScale. So that we don't trigger
+            // re-flow while toggling between overview mode and normal mode.
+            nativeSetSize(width, Math.round((float) width * h / w),
+                    Math.round(mWebView.mInZoomOverview ? w * scale
+                            / mWebView.mLastScale : w), scale, w, h);
         } else {
             nativeSetSize(w, h, w, scale, w, h);
         }
@@ -1389,6 +1463,21 @@
         }
     }
 
+    // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
+    // callbacks. Computes the sum of database quota for all origins.
+    private long getUsedQuota() {
+        WebStorage webStorage = WebStorage.getInstance();
+        Set<String> origins = webStorage.getOrigins();
+        if (origins == null) {
+            return 0;
+        }
+        long usedQuota = 0;
+        for (String origin : origins) {
+            usedQuota += webStorage.getQuotaForOrigin(origin);
+        }
+        return usedQuota;
+    }
+
     // Used to avoid posting more than one draw message.
     private boolean mDrawIsScheduled;
 
@@ -1409,6 +1498,7 @@
         public Region mInvalRegion;
         public Point mViewPoint;
         public Point mWidthHeight;
+        public int mMinPrefWidth;
     }
 
     private void webkitDraw() {
@@ -1424,6 +1514,9 @@
             // Send the native view size that was used during the most recent
             // layout.
             draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
+            if (WebView.ENABLE_DOUBLETAP_ZOOM && mSettings.getUseWideViewPort()) {
+                draw.mMinPrefWidth = nativeGetContentMinPrefWidth();
+            }
             if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
             Message.obtain(mWebView.mPrivateHandler,
                     WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
@@ -1734,9 +1827,10 @@
 
             if (mRestoredScale > 0) {
                 Message.obtain(mWebView.mPrivateHandler,
-                        WebView.DID_FIRST_LAYOUT_MSG_ID, mRestoredScale, 0,
+                        WebView.DID_FIRST_LAYOUT_MSG_ID,
+                        mRestoredScreenWidthScale > 0 ?
+                        -mRestoredScreenWidthScale : mRestoredScale, 0,
                         scaleLimit).sendToTarget();
-                mRestoredScale = 0;
             } else {
                 // if standardLoad is true, use mViewportInitialScale, otherwise
                 // pass -1 to the WebView to indicate no change of the scale.
@@ -1764,8 +1858,8 @@
                 }
             }
 
-            // reset restored offset
-            mRestoredX = mRestoredY = 0;
+            // reset restored offset, scale
+            mRestoredX = mRestoredY = mRestoredScale = mRestoredScreenWidthScale = 0;
         }
     }
 
@@ -1777,6 +1871,17 @@
     }
 
     // called by JNI
+    private void restoreScreenWidthScale(int scale) {
+        if (!WebView.ENABLE_DOUBLETAP_ZOOM || !mSettings.getUseWideViewPort()) {
+            return;
+        }
+
+        if (mBrowserFrame.firstLayoutDone() == false) {
+            mRestoredScreenWidthScale = scale;
+        }
+    }
+
+    // called by JNI
     private void needTouchEvents(boolean need) {
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index e613ec4..ea88b5b 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -104,6 +104,7 @@
     private View mDropDownAnchorView;  // view is retrieved lazily from id once needed
     private int mDropDownWidth;
     private int mDropDownHeight;
+    private final Rect mTempRect = new Rect();
 
     private Drawable mDropDownListHighlight;
 
@@ -209,8 +210,7 @@
     private void onClickImpl() {
         // If the dropdown is showing, bring it back in front of the soft
         // keyboard when the user touches the text field.
-        if (mPopup.isShowing() &&
-                mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED) {
+        if (mPopup.isShowing() && isInputMethodNotNeeded()) {
             ensureImeVisible();
         }
     }
@@ -1089,6 +1089,13 @@
     }
 
     /**
+     * @hide internal used only here and SearchDialog
+     */
+    public boolean isInputMethodNotNeeded() {
+        return mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
+    }
+
+    /**
      * <p>Displays the drop down on screen.</p>
      */
     public void showDropDown() {
@@ -1097,7 +1104,7 @@
         int widthSpec = 0;
         int heightSpec = 0;
 
-        boolean noInputMethod = mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
+        boolean noInputMethod = isInputMethodNotNeeded();
 
         if (mPopup.isShowing()) {
             if (mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT) {
@@ -1303,7 +1310,15 @@
                 getDropDownAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations);
 
         if (mDropDownAlwaysVisible) {
-            return maxHeight;
+            // getMaxAvailableHeight() subtracts the padding, so we put it back,
+            // to get the available height for the whole window
+            int padding = 0;
+            Drawable background = mPopup.getBackground();
+            if (background != null) {
+                background.getPadding(mTempRect);
+                padding = mTempRect.top + mTempRect.bottom;
+            }
+            return maxHeight + padding;
         }
 
         return mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED,
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 24c0e2a..e19a93d 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -432,7 +432,7 @@
             width = resolveSize(width, widthMeasureSpec);
 
             if (offsetHorizontalAxis) {
-                    for (int i = 0; i < count; i++) {
+                for (int i = 0; i < count; i++) {
                     View child = getChildAt(i);
                     if (child.getVisibility() != GONE) {
                         LayoutParams params = (LayoutParams) child.getLayoutParams();
@@ -486,10 +486,14 @@
                     View child = getChildAt(i);
                     if (child.getVisibility() != GONE && child != ignore) {
                         LayoutParams params = (LayoutParams) child.getLayoutParams();
-                        params.mLeft += horizontalOffset;
-                        params.mRight += horizontalOffset;
-                        params.mTop += verticalOffset;
-                        params.mBottom += verticalOffset;
+                        if (horizontalGravity) {
+                            params.mLeft += horizontalOffset;
+                            params.mRight += horizontalOffset;
+                        }
+                        if (verticalGravity) {
+                            params.mTop += verticalOffset;
+                            params.mBottom += verticalOffset;
+                        }
                     }
                 }
             }
diff --git a/core/java/com/google/android/gdata/client/QueryParamsImpl.java b/core/java/com/google/android/gdata/client/QueryParamsImpl.java
index e27b36f..fbe0d22 100644
--- a/core/java/com/google/android/gdata/client/QueryParamsImpl.java
+++ b/core/java/com/google/android/gdata/client/QueryParamsImpl.java
@@ -60,6 +60,8 @@
             sb.append('?');
         }
         for (String param : params) {
+            String value = mParams.get(param);
+            if (value == null) continue;
             if (first) {
                 first = false;
             } else {
@@ -67,7 +69,7 @@
             }
             sb.append(param);
             sb.append('=');
-            String value = mParams.get(param);
+
             String encodedValue = null;
 
             try {
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 6449147..65c0435 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -53,10 +53,11 @@
     int       STREAM_MUSIC;          //...  stream type constants
     int       STREAM_ALARM;          //...  stream type constants
     int       STREAM_NOTIFICATION;   //...  stream type constants
-    int       STREAM_BLUETOOTH_SCO;   //...  stream type constants
+    int       STREAM_BLUETOOTH_SCO;  //...  stream type constants
+    int       STREAM_DTMF;           //...  stream type constants
     int       MODE_STREAM;           //...  memory mode
     int       MODE_STATIC;           //...  memory mode
-    jfieldID  nativeTrackInJavaObj; // stores in Java the native AudioTrack object
+    jfieldID  nativeTrackInJavaObj;  // stores in Java the native AudioTrack object
     jfieldID  jniData;      // stores in Java additional resources used by the native AudioTrack
 };
 static fields_t javaAudioTrackFields;
@@ -203,6 +204,8 @@
         atStreamType = AudioSystem::NOTIFICATION;
     } else if (streamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) {
         atStreamType = AudioSystem::BLUETOOTH_SCO;
+    } else if (streamType == javaAudioTrackFields.STREAM_DTMF) {
+        atStreamType = AudioSystem::DTMF;
     } else {
         LOGE("Error creating AudioTrack: unknown stream type.");
         return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE;
@@ -727,6 +730,8 @@
         nativeStreamType = AudioSystem::NOTIFICATION;
     } else if (javaStreamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) {
         nativeStreamType = AudioSystem::BLUETOOTH_SCO;
+    } else if (javaStreamType == javaAudioTrackFields.STREAM_DTMF) {
+        nativeStreamType = AudioSystem::DTMF;
     } else {
         nativeStreamType = AudioSystem::DEFAULT;
     }
@@ -822,6 +827,7 @@
 #define JAVA_CONST_STREAM_ALARM_NAME                    "STREAM_ALARM"
 #define JAVA_CONST_STREAM_NOTIFICATION_NAME             "STREAM_NOTIFICATION"
 #define JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME            "STREAM_BLUETOOTH_SCO"
+#define JAVA_CONST_STREAM_DTMF_NAME                     "STREAM_DTMF"
 #define JAVA_CONST_MODE_STREAM_NAME                     "MODE_STREAM"
 #define JAVA_CONST_MODE_STATIC_NAME                     "MODE_STATIC"
 #define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME            "mNativeTrackInJavaObj"
@@ -943,8 +949,10 @@
                JAVA_CONST_STREAM_NOTIFICATION_NAME, &(javaAudioTrackFields.STREAM_NOTIFICATION))
           || !android_media_getIntConstantFromClass(env, audioManagerClass,
                JAVA_AUDIOMANAGER_CLASS_NAME,
-               JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME,
-               &(javaAudioTrackFields.STREAM_BLUETOOTH_SCO))) {
+               JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME, &(javaAudioTrackFields.STREAM_BLUETOOTH_SCO))
+          || !android_media_getIntConstantFromClass(env, audioManagerClass,
+               JAVA_AUDIOMANAGER_CLASS_NAME,
+               JAVA_CONST_STREAM_DTMF_NAME, &(javaAudioTrackFields.STREAM_DTMF))) {
        // error log performed in android_media_getIntConstantFromClass()
        return -1;
     }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 17704ae..6ad2441 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -863,7 +863,8 @@
         android:protectionLevel="signature" />
 
     <!-- Allows an application to call the activity manager shutdown() API
-         to put the higher-level system there into a shutdown state. -->
+         to put the higher-level system there into a shutdown state.
+         @hide -->
     <permission android:name="android.permission.SHUTDOWN"
         android:label="@string/permlab_shutdown"
         android:description="@string/permdesc_shutdown"
@@ -872,7 +873,8 @@
     <!-- Allows an application to tell the activity manager to temporarily
          stop application switches, putting it into a special mode that
          prevents applications from immediately switching away from some
-         critical UI such as the home screen. -->
+         critical UI such as the home screen.
+         @hide -->
     <permission android:name="android.permission.STOP_APP_SWITCHES"
         android:label="@string/permlab_stopAppSwitches"
         android:description="@string/permdesc_stopAppSwitches"
diff --git a/core/res/res/drawable/expander_ic_maximized.9.png b/core/res/res/drawable/expander_ic_maximized.9.png
index 778255a..465cabd 100644
--- a/core/res/res/drawable/expander_ic_maximized.9.png
+++ b/core/res/res/drawable/expander_ic_maximized.9.png
Binary files differ
diff --git a/core/res/res/drawable/expander_ic_minimized.9.png b/core/res/res/drawable/expander_ic_minimized.9.png
index 5235c18..9967ecb 100644
--- a/core/res/res/drawable/expander_ic_minimized.9.png
+++ b/core/res/res/drawable/expander_ic_minimized.9.png
Binary files differ
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0e848398..549b668 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1709,6 +1709,8 @@
     <string name="volume_music">Media volume</string>
     <!-- Hint shown in the volume toast to inform the user that the media audio is playing through Bluetooth. -->
     <string name="volume_music_hint_playing_through_bluetooth">Playing through Bluetooth</string>
+    <!-- Hint shown in the volume toast to inform the user that the current ringtone is the silent ringtone. -->
+    <string name="volume_music_hint_silent_ringtone_selected">Silent ringtone selected</string>
     <!-- Title of the dialog where the user is adjusting the phone call volume -->
     <string name="volume_call">In-call volume</string>
     <!-- Title of the dialog where the user is adjusting the phone call volume when connected on bluetooth-->
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index f829b08..2b9e448 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -96,6 +96,7 @@
     native private int  nAllocationCreatePredefSized(int predef, int count);
     native private int  nAllocationCreateSized(int elem, int count);
     native private int  nAllocationCreateFromBitmap(int dstFmt, boolean genMips, Bitmap bmp);
+    native private int  nAllocationCreateFromBitmapBoxed(int dstFmt, boolean genMips, Bitmap bmp);
 
     native private void nAllocationUploadToTexture(int alloc, int baseMioLevel);
     native private void nAllocationDestroy(int alloc);
@@ -529,7 +530,12 @@
     }
 
     public Allocation allocationCreateFromBitmap(Bitmap b, ElementPredefined dstFmt, boolean genMips) {
-        int id = nAllocationCreateFromBitmap(dstFmt.mID, genMips, b); 
+        int id = nAllocationCreateFromBitmap(dstFmt.mID, genMips, b);
+        return new Allocation(id);
+    }
+
+    public Allocation allocationCreateFromBitmapBoxed(Bitmap b, ElementPredefined dstFmt, boolean genMips) {
+        int id = nAllocationCreateFromBitmapBoxed(dstFmt.mID, genMips, b);
         return new Allocation(id);
     }
 
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 573610c..6f781a0 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -291,6 +291,29 @@
     return 0;
 }
 
+static int
+nAllocationCreateFromBitmapBoxed(JNIEnv *_env, jobject _this, jint dstFmt, jboolean genMips, jobject jbitmap)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    SkBitmap const * nativeBitmap =
+            (SkBitmap const *)_env->GetIntField(jbitmap, gNativeBitmapID);
+    const SkBitmap& bitmap(*nativeBitmap);
+    SkBitmap::Config config = bitmap.getConfig();
+
+    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)rsAllocationCreateFromBitmapBoxed(w, h, (RsElementPredefined)dstFmt, e, genMips, ptr);
+        bitmap.unlockPixels();
+        return id;
+    }
+    return 0;
+}
+
 
 static void
 nAllocationDestroy(JNIEnv *_env, jobject _this, jint a)
@@ -994,6 +1017,7 @@
 {"nAllocationCreatePredefSized",   "(II)I",                                (void*)nAllocationCreatePredefSized },
 {"nAllocationCreateSized",         "(II)I",                                (void*)nAllocationCreateSized },
 {"nAllocationCreateFromBitmap",    "(IZLandroid/graphics/Bitmap;)I",       (void*)nAllocationCreateFromBitmap },
+{"nAllocationCreateFromBitmapBoxed","(IZLandroid/graphics/Bitmap;)I",       (void*)nAllocationCreateFromBitmapBoxed },
 {"nAllocationUploadToTexture",     "(II)V",                                (void*)nAllocationUploadToTexture },
 {"nAllocationDestroy",             "(I)V",                                 (void*)nAllocationDestroy },
 {"nAllocationData",                "(I[I)V",                               (void*)nAllocationData_i },
diff --git a/include/media/stagefright/OMXDecoder.h b/include/media/stagefright/OMXDecoder.h
index 0859457..e76fd4c 100644
--- a/include/media/stagefright/OMXDecoder.h
+++ b/include/media/stagefright/OMXDecoder.h
@@ -26,6 +26,8 @@
 #include <utils/List.h>
 #include <utils/threads.h>
 
+#include <OMX_Video.h>
+
 namespace android {
 
 class OMXMediaBuffer;
@@ -35,10 +37,8 @@
                    public MediaBufferObserver {
 public:
     static OMXDecoder *Create(
-            OMXClient *client, const sp<MetaData> &data);
-
-    static OMXDecoder *CreateEncoder(
-            OMXClient *client, const sp<MetaData> &data);
+            OMXClient *client, const sp<MetaData> &data,
+            bool createEncoder = false);
 
     virtual ~OMXDecoder();
 
@@ -68,10 +68,23 @@
     };
 
     enum PortStatus {
-        kPortStatusActive   = 0,
-        kPortStatusDisabled = 1,
-        kPortStatusShutdown = 2,
-        kPortStatusFlushing = 3
+        kPortStatusActive             = 0,
+        kPortStatusDisabled           = 1,
+        kPortStatusShutdown           = 2,
+        kPortStatusFlushing           = 3,
+        kPortStatusFlushingToDisabled = 4,
+        kPortStatusFlushingToShutdown = 5,
+    };
+
+    enum Quirks {
+        kWantsRawNALFrames                   = 1,
+        kDoesntReturnBuffersOnDisable        = 2,
+        kDoesntFlushOnExecutingToIdle        = 4,
+        kDoesntProperlyFlushAllPortsAtOnce   = 8,
+        kRequiresAllocateBufferOnInputPorts  = 16,
+        kRequiresAllocateBufferOnOutputPorts = 32,
+        kRequiresLoadedToIdleAfterAllocation = 64,
+        kMeasuresTimeInMilliseconds          = 128,
     };
 
     OMXClient *mClient;
@@ -79,6 +92,8 @@
     IOMX::node_id mNode;
     char *mComponentName;
     bool mIsMP3;
+    bool mIsAVC;
+    uint32_t mQuirks;
 
     MediaSource *mSource;
     sp<MetaData> mOutputFormat;
@@ -116,7 +131,8 @@
     bool mReachedEndOfInput;
 
     OMXDecoder(OMXClient *client, IOMX::node_id node,
-               const char *mime, const char *codec);
+               const char *mime, const char *codec,
+               uint32_t quirks);
 
     void setPortStatus(OMX_U32 port_index, PortStatus status);
     PortStatus getPortStatus(OMX_U32 port_index) const;
@@ -125,7 +141,13 @@
 
     void setAMRFormat();
     void setAACFormat();
-    void setVideoOutputFormat(OMX_U32 width, OMX_U32 height);
+
+    status_t setVideoPortFormatType(
+            OMX_U32 portIndex,
+            OMX_VIDEO_CODINGTYPE compressionFormat,
+            OMX_COLOR_FORMATTYPE colorFormat);
+
+    void setVideoOutputFormat(const char *mime, OMX_U32 width, OMX_U32 height);
     void setup();
     void dumpPortDefinition(OMX_U32 port_index);
 
@@ -144,6 +166,7 @@
 
     void freeInputBuffer(IOMX::buffer_id buffer);
     void freeOutputBuffer(IOMX::buffer_id buffer);
+    void freePortBuffers(OMX_U32 port_index);
 
     void postStart();
     void postEmptyBufferDone(IOMX::buffer_id buffer);
diff --git a/include/media/stagefright/TIHardwareRenderer.h b/include/media/stagefright/TIHardwareRenderer.h
new file mode 100644
index 0000000..f7fa81b
--- /dev/null
+++ b/include/media/stagefright/TIHardwareRenderer.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TI_HARDWARE_RENDERER_H_
+
+#define TI_HARDWARE_RENDERER_H_
+
+#include <media/stagefright/VideoRenderer.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class ISurface;
+class Overlay;
+
+class TIHardwareRenderer : public VideoRenderer {
+public:
+    TIHardwareRenderer(
+            const sp<ISurface> &surface,
+            size_t displayWidth, size_t displayHeight,
+            size_t decodedWidth, size_t decodedHeight);
+
+    virtual ~TIHardwareRenderer();
+
+    virtual void render(
+            const void *data, size_t size, void *platformPrivate);
+
+private:
+    sp<ISurface> mISurface;
+    size_t mDisplayWidth, mDisplayHeight;
+    size_t mDecodedWidth, mDecodedHeight;
+    size_t mFrameSize;
+    sp<Overlay> mOverlay;
+    Vector<void *> mOverlayAddresses;
+    size_t mIndex;
+
+    TIHardwareRenderer(const TIHardwareRenderer &);
+    TIHardwareRenderer &operator=(const TIHardwareRenderer &);
+};
+
+}  // namespace android
+
+#endif  // TI_HARDWARE_RENDERER_H_
+
diff --git a/keystore/jni/certtool.c b/keystore/jni/certtool.c
index 1ae8dab..b36b34a 100644
--- a/keystore/jni/certtool.c
+++ b/keystore/jni/certtool.c
@@ -30,13 +30,17 @@
 android_security_CertTool_generateCertificateRequest(JNIEnv* env,
                                                      jobject thiz,
                                                      jint bits,
-                                                     jstring subject)
+                                                     jstring jChallenge)
 
 {
+    int ret = -1;
+    jboolean bIsCopy;
     char csr[REPLY_MAX];
-    if (gen_csr(bits, subject, csr) == 0) {
-        return (*env)->NewStringUTF(env, csr);
-    }
+    const char* challenge = (*env)->GetStringUTFChars(env, jChallenge, &bIsCopy);
+
+    ret = gen_csr(bits, challenge , csr);
+    (*env)->ReleaseStringUTFChars(env, jChallenge, challenge);
+    if (ret == 0) return (*env)->NewStringUTF(env, csr);
     return NULL;
 }
 
diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk
index c10e02b..f5c03bb 100644
--- a/libs/audioflinger/Android.mk
+++ b/libs/audioflinger/Android.mk
@@ -107,6 +107,7 @@
 
 ifeq ($(BOARD_HAVE_BLUETOOTH),true)
   LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP
+  LOCAL_SHARED_LIBRARIES += liba2dp
 endif
 
 ifeq ($(AUDIO_POLICY_TEST),true)
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 1336131..232ffb0 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -3443,8 +3443,8 @@
             srcThread->getTracks(tracks, activeTracks, stream);
             if (tracks.size()) {
                 dstThread->putTracks(tracks, activeTracks);
-                dstThread->sendConfigEvent(AudioSystem::STREAM_CONFIG_CHANGED, stream);
             }
+            dstThread->sendConfigEvent(AudioSystem::STREAM_CONFIG_CHANGED, stream);
         }
     }
 
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 855ea63..1d14f70 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -146,26 +146,26 @@
 };
 
 enum RsBlendSrcFunc {
-    RS_BLEND_SRC_ZERO, 
-    RS_BLEND_SRC_ONE, 
-    RS_BLEND_SRC_DST_COLOR, 
-    RS_BLEND_SRC_ONE_MINUS_DST_COLOR, 
-    RS_BLEND_SRC_SRC_ALPHA, 
-    RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA, 
-    RS_BLEND_SRC_DST_ALPHA, 
-    RS_BLEND_SRC_ONE_MINUS_DST_ALPHA, 
-    RS_BLEND_SRC_SRC_ALPHA_SATURATE
+    RS_BLEND_SRC_ZERO,                  // 0
+    RS_BLEND_SRC_ONE,                   // 1
+    RS_BLEND_SRC_DST_COLOR,             // 2
+    RS_BLEND_SRC_ONE_MINUS_DST_COLOR,   // 3
+    RS_BLEND_SRC_SRC_ALPHA,             // 4
+    RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA,   // 5
+    RS_BLEND_SRC_DST_ALPHA,             // 6
+    RS_BLEND_SRC_ONE_MINUS_DST_ALPHA,   // 7
+    RS_BLEND_SRC_SRC_ALPHA_SATURATE     // 8
 };
 
 enum RsBlendDstFunc {
-    RS_BLEND_DST_ZERO, 
-    RS_BLEND_DST_ONE, 
-    RS_BLEND_DST_SRC_COLOR, 
-    RS_BLEND_DST_ONE_MINUS_SRC_COLOR, 
-    RS_BLEND_DST_SRC_ALPHA, 
-    RS_BLEND_DST_ONE_MINUS_SRC_ALPHA, 
-    RS_BLEND_DST_DST_ALPHA, 
-    RS_BLEND_DST_ONE_MINUS_DST_ALPHA
+    RS_BLEND_DST_ZERO,                  // 0
+    RS_BLEND_DST_ONE,                   // 1
+    RS_BLEND_DST_SRC_COLOR,             // 2
+    RS_BLEND_DST_ONE_MINUS_SRC_COLOR,   // 3
+    RS_BLEND_DST_SRC_ALPHA,             // 4
+    RS_BLEND_DST_ONE_MINUS_SRC_ALPHA,   // 5
+    RS_BLEND_DST_DST_ALPHA,             // 6
+    RS_BLEND_DST_ONE_MINUS_DST_ALPHA    // 7
 };
 
 enum RsTexEnvMode {
diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h
index 0789301..7a5556e 100644
--- a/libs/rs/RenderScriptEnv.h
+++ b/libs/rs/RenderScriptEnv.h
@@ -30,11 +30,3 @@
 #define RS_PROGRAM_VERTEX_PROJECTION_OFFSET 16
 #define RS_PROGRAM_VERTEX_TEXTURE_OFFSET 32
 
-//typedef int (*rsc_RunScript)(uint32_t launchIndex, const rsc_FunctionTable *);
-
-
-/* EnableCap */
-#define GL_LIGHTING                       0x0B50
-
-/* LightName */
-#define GL_LIGHT0                         0x4000
diff --git a/libs/rs/java/Film/res/raw/filmstrip.c b/libs/rs/java/Film/res/raw/filmstrip.c
index 6885251..495fe55 100644
--- a/libs/rs/java/Film/res/raw/filmstrip.c
+++ b/libs/rs/java/Film/res/raw/filmstrip.c
@@ -11,29 +11,29 @@
     int32_t triangleOffsets[64];
     float triangleOffsetsTex[64];
     int32_t triangleOffsetsCount;
-} FilmScriptUserEnv; 
-*/ 
+} FilmScriptUserEnv;
+*/
+
+#define POS_TRANSLATE 0
+#define POS_ROTATE 1
+#define POS_FOCUS 2
+
+#define STATE_TRIANGLE_OFFSET_COUNT 0
+#define STATE_LAST_FOCUS 1
+
 
 // The script enviroment has 3 env allocations.
 // bank0: (r) The enviroment structure
 // bank1: (r) The position information
 // bank2: (rw) The temporary texture state
 
-int main(int index) 
+int main(int index)
 {
     int f1,f2,f3,f4, f5,f6,f7,f8, f9,f10,f11,f12, f13,f14,f15,f16;
     int g1,g2,g3,g4, g5,g6,g7,g8, g9,g10,g11,g12, g13,g14,g15,g16;
-    float trans;
-    float rot;
-    int x;
-    float focusPos;  // float
-    int focusID;
-    int lastFocusID;
-    int imgCount;
 
-    trans = loadF(1, 0);
-    rot = loadF(1, 1);
-
+    float trans = loadF(1, POS_TRANSLATE);
+    float rot = loadF(1, POS_ROTATE);
     matrixLoadScale(&f16, 2.f, 2.f, 2.f);
     matrixTranslate(&f16, 0.f, 0.f, trans);
     matrixRotate(&f16, 90.f, 0.f, 0.f, 1.f);
@@ -46,24 +46,18 @@
     drawTriangleMesh(NAMED_mesh);
 
 
-
-    //int imgId = 0;
-
+    // Start of images.
     bindProgramFragmentStore(NAMED_PFImages);
     bindProgramFragment(NAMED_PFSImages);
     bindProgramVertex(NAMED_PVImages);
 
-    //focusPos = loadF(1, 2);
-    //focusID = 0;
-    //lastFocusID = loadI32(2, 0);
-    //imgCount = 13;
+    float focusPos = loadF(1, POS_FOCUS);
+    int focusID = 0;
+    int lastFocusID = loadI32(2, STATE_LAST_FOCUS);
+    int imgCount = 13;
 
-    /*
-    disable(GL_LIGHTING);
-
-
-    if (trans > (-.3)) {
-        focusID = -1.0 - focusPos;
+    if (trans > (-.3f)) {
+        focusID = -1.0f - focusPos;
         if (focusID >= imgCount) {
             focusID = -1;
         }
@@ -71,6 +65,7 @@
         focusID = -1;
     }
 
+    /*
     if (focusID != lastFocusID) {
         if (lastFocusID >= 0) {
             uploadToTexture(con, env->tex[lastFocusID], 1);
@@ -79,36 +74,38 @@
             uploadToTexture(con, env->tex[focusID], 0);
         }
     }
-    storeEnvI32(con, 2, 0, focusID);
+    */
+    storeI32(2, STATE_LAST_FOCUS, focusID);
 
+    int triangleOffsetsCount = loadI32(2, STATE_TRIANGLE_OFFSET_COUNT);
 
+    int imgId = 0;
     for (imgId=1; imgId <= imgCount; imgId++) {
-        float pos = focusPos + imgId + .4f;
-        int offset = (int)floor(pos*2);
-        pos -= 0.75;
-    
-        offset += env->triangleOffsetsCount / 2;
-    
-        if ((offset < 0) || (offset >= env->triangleOffsetsCount)) {
-            continue;
+        float pos = focusPos + imgId + 0.4f;
+        int offset = (int)floorf(pos * 2.f);
+        pos = pos - 0.75f;
+
+        offset = offset + triangleOffsetsCount / 2;
+
+        if (!((offset < 0) || (offset >= triangleOffsetsCount))) {
+            int start = offset -2;
+            int end = offset + 2;
+
+            if (start < 0) {
+                start = 0;
+            }
+            if (end > triangleOffsetsCount) {
+                end = triangleOffsetsCount;
+            }
+
+            bindTexture(NAMED_PFImages, 0, loadI32(0, imgId - 1));
+    /*
+            matrixLoadTranslate(con, &m, -pos - env->triangleOffsetsTex[env->triangleOffsetsCount / 2], 0, 0);
+            storeEnvMatrix(con, 3, RS_PROGRAM_VERTEX_TEXTURE_OFFSET, &m);
+            renderTriangleMeshRange(con, env->mesh, env->triangleOffsets[start], env->triangleOffsets[end] - env->triangleOffsets[start]);
+    */
         }
-    
-        int start = offset -2;
-        int end = offset + 2;
-    
-        if (start < 0) {
-            start = 0;
-        }
-        if (end > env->triangleOffsetsCount) {
-            end = env->triangleOffsetsCount;
-        }
-    
-        programFragmentBindTexture(con, env->fpImages, 0, env->tex[imgId - 1]);
-        matrixLoadTranslate(con, &m, -pos - env->triangleOffsetsTex[env->triangleOffsetsCount / 2], 0, 0); 
-        storeEnvMatrix(con, 3, RS_PROGRAM_VERTEX_TEXTURE_OFFSET, &m);
-        renderTriangleMeshRange(con, env->mesh, env->triangleOffsets[start], env->triangleOffsets[end] - env->triangleOffsets[start]);
-    } 
-*/
+    }
     return 0;
 }
 
diff --git a/libs/rs/java/Film/src/com/android/film/FilmRS.java b/libs/rs/java/Film/src/com/android/film/FilmRS.java
index fca0818..395bd35 100644
--- a/libs/rs/java/Film/src/com/android/film/FilmRS.java
+++ b/libs/rs/java/Film/src/com/android/film/FilmRS.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
@@ -38,6 +39,12 @@
 import android.view.MotionEvent;
 
 public class FilmRS {
+    private final int POS_TRANSLATE = 0;
+    private final int POS_ROTATE = 1;
+    private final int POS_FOCUS = 2;
+
+    private final int STATE_TRIANGLE_OFFSET_COUNT = 0;
+    private final int STATE_LAST_FOCUS = 1;
 
     public FilmRS() {
     }
@@ -56,11 +63,11 @@
         if (x > 270) {
             x = 270;
         }
-    
+
         float anim = ((float)x-50) / 270.f;
-        mBufferPos[0] = 2f * anim + 0.5f;   // translation
-        mBufferPos[1] = (anim * 40);  // rotation
-        mBufferPos[2] = ((float)y) / 16.f - 8;  // focusPos
+        mBufferPos[POS_TRANSLATE] = 2f * anim + 0.5f;   // translation
+        mBufferPos[POS_ROTATE] = (anim * 40);  // rotation
+        mBufferPos[POS_FOCUS] = ((float)y) / 16.f - 8;  // focusPos
         mAllocPos.data(mBufferPos);
     }
 
@@ -80,15 +87,19 @@
     private RenderScript.ProgramVertex mPVImages;
     private ProgramVertexAlloc mPVA;
 
-    private RenderScript.Allocation mAllocEnv;
+    private RenderScript.Allocation mImages[];
+    private RenderScript.Allocation mAllocIDs;
     private RenderScript.Allocation mAllocPos;
     private RenderScript.Allocation mAllocState;
     private RenderScript.Allocation mAllocPV;
     private RenderScript.TriangleMesh mMesh;
     private RenderScript.Light mLight;
 
-    private float[] mBufferPos;
-    private float[] mBufferPV;
+    private FilmStripMesh mFSM;
+
+    private int[] mBufferIDs;
+    private float[] mBufferPos = new float[3];
+    private int[] mBufferState;
 
     private void initSamplers() {
         mRS.samplerBegin();
@@ -112,7 +123,7 @@
         mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.EQUAL);
         mRS.programFragmentStoreDitherEnable(false);
         mRS.programFragmentStoreDepthMask(false);
-        mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.ONE, 
+        mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.ONE,
                                           RenderScript.BlendDstFunc.ONE);
         mPFSImages = mRS.programFragmentStoreCreate();
         mPFSImages.setName("PFSImages");
@@ -148,7 +159,75 @@
     }
 
 
-    int mParams[] = new int[10];
+    private void loadImages() {
+        mBufferIDs = new int[13];
+        mImages = new RenderScript.Allocation[13];
+        mAllocIDs = mRS.allocationCreatePredefSized(
+            RenderScript.ElementPredefined.USER_FLOAT,
+            mBufferIDs.length);
+
+        Bitmap b;
+        BitmapFactory.Options opts = new BitmapFactory.Options();
+        opts.inScaled = false;
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p01, opts);
+        mImages[0] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p02, opts);
+        mImages[1] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p03, opts);
+        mImages[2] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p04, opts);
+        mImages[3] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p05, opts);
+        mImages[4] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p06, opts);
+        mImages[5] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p07, opts);
+        mImages[6] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p08, opts);
+        mImages[7] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p09, opts);
+        mImages[8] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p10, opts);
+        mImages[9] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p11, opts);
+        mImages[10] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p12, opts);
+        mImages[11] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p13, opts);
+        mImages[12] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        for(int ct=0; ct < mImages.length; ct++) {
+            mImages[ct].uploadToTexture(1);
+            mBufferIDs[ct] = mImages[ct].getID();
+        }
+        mAllocIDs.data(mBufferIDs);
+    }
+
+    private void initState()
+    {
+        mBufferState = new int[10];
+        mAllocState = mRS.allocationCreatePredefSized(
+            RenderScript.ElementPredefined.USER_FLOAT,
+            mBufferState.length);
+
+        mBufferState[STATE_TRIANGLE_OFFSET_COUNT] = mFSM.mTriangleOffsetsCount;
+        mBufferState[STATE_LAST_FOCUS] = -1;
+
+        mAllocState.data(mBufferState);
+    }
 
     private void initRS() {
         mElementVertex = mRS.elementGetPredefined(
@@ -157,8 +236,8 @@
             RenderScript.ElementPredefined.INDEX_16);
 
         mRS.triangleMeshBegin(mElementVertex, mElementIndex);
-        FilmStripMesh fsm = new FilmStripMesh();
-        fsm.init(mRS);
+        mFSM = new FilmStripMesh();
+        mFSM.init(mRS);
         mMesh = mRS.triangleMeshCreate();
         mMesh.setName("mesh");
 
@@ -176,19 +255,22 @@
         mRS.scriptCSetRoot(true);
         mScriptStrip = mRS.scriptCCreate();
 
-        mBufferPos = new float[3];
         mAllocPos = mRS.allocationCreatePredefSized(
-            RenderScript.ElementPredefined.USER_FLOAT, 
+            RenderScript.ElementPredefined.USER_FLOAT,
             mBufferPos.length);
 
+        loadImages();
+        initState();
+
         mPVA = new ProgramVertexAlloc(mRS);
         mPVBackground.bindAllocation(0, mPVA.mAlloc);
         mPVImages.bindAllocation(0, mPVA.mAlloc);
         mPVA.setupProjectionNormalized(320, 480);
 
 
+        mScriptStrip.bindAllocation(mAllocIDs, 0);
         mScriptStrip.bindAllocation(mAllocPos, 1);
-       //mScriptStrip.bindAllocation(gStateAlloc, 2);
+        mScriptStrip.bindAllocation(mAllocState, 2);
         mScriptStrip.bindAllocation(mPVA.mAlloc, 3);
 
 
diff --git a/libs/rs/java/Rollo/res/raw/calendar.png b/libs/rs/java/Rollo/res/raw/calendar.png
new file mode 100644
index 0000000..030ae73
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/calendar.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/g1155.png b/libs/rs/java/Rollo/res/raw/g1155.png
new file mode 100644
index 0000000..68e1843
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/g1155.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/g2140.png b/libs/rs/java/Rollo/res/raw/g2140.png
new file mode 100644
index 0000000..8c4e853
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/g2140.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path1920.png b/libs/rs/java/Rollo/res/raw/path1920.png
new file mode 100644
index 0000000..3510665
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path1920.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path1927.png b/libs/rs/java/Rollo/res/raw/path1927.png
new file mode 100644
index 0000000..fccc846
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path1927.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path3099.png b/libs/rs/java/Rollo/res/raw/path3099.png
new file mode 100644
index 0000000..527ebf6
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path3099.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path3950.png b/libs/rs/java/Rollo/res/raw/path3950.png
new file mode 100644
index 0000000..59a646a
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path3950.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path431.png b/libs/rs/java/Rollo/res/raw/path431.png
new file mode 100644
index 0000000..5d2ed75
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path431.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path4481.png b/libs/rs/java/Rollo/res/raw/path4481.png
new file mode 100644
index 0000000..78be0fc
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path4481.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path5168.png b/libs/rs/java/Rollo/res/raw/path5168.png
new file mode 100644
index 0000000..a7c3a19
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path5168.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path676.png b/libs/rs/java/Rollo/res/raw/path676.png
new file mode 100644
index 0000000..2099690
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path676.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path754.png b/libs/rs/java/Rollo/res/raw/path754.png
new file mode 100644
index 0000000..88aed5b
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path754.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path815.png b/libs/rs/java/Rollo/res/raw/path815.png
new file mode 100644
index 0000000..407570f
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path815.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/polygon2408.png b/libs/rs/java/Rollo/res/raw/polygon2408.png
new file mode 100644
index 0000000..4413954
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/polygon2408.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/rollo.c b/libs/rs/java/Rollo/res/raw/rollo.c
index d338d0d..9e03a44 100644
--- a/libs/rs/java/Rollo/res/raw/rollo.c
+++ b/libs/rs/java/Rollo/res/raw/rollo.c
@@ -19,6 +19,7 @@
 #define STATE_COUNT             8
 #define STATE_TOUCH             9
 
+
 float filter(float val, float target, float str)
 {
     float delta = (target - val);
@@ -51,7 +52,7 @@
 
     float touchCut = 1.f;
     if (loadI32(0, STATE_TOUCH)) {
-        touchCut = 5.f;
+        touchCut = 4.f;
     }
 
 
@@ -60,20 +61,24 @@
     storeF(2, SCRATCH_ZOOM, zoom);
 
     float targetRot = loadI32(0, STATE_FIRST_VISIBLE) / 180.0f * 3.14f;
-    float rot = filter(loadF(2, SCRATCH_ROT), targetRot, 0.1f * touchCut);
-    storeF(2, SCRATCH_ROT, rot);
+    float drawRot = filter(loadF(2, SCRATCH_ROT), targetRot, 0.1f * touchCut);
+    storeF(2, SCRATCH_ROT, drawRot);
 
-    float diam = 8.f;// + curve * 2.f;
+    float diam = 8.f;
     float scale = 1.0f / zoom;
 
-    rot = rot * scale;
-    float rotStep = 20.0f / 180.0f * 3.14f * scale;
+    // Bug makes 1.0f alpha fail.
+    color(1.0f, 1.0f, 1.0f, 0.99f);
+
+    float rot = drawRot * scale;
+    float rotStep = 16.0f / 180.0f * 3.14f * scale;
     rowCount = 4;
     int index = 0;
     int iconCount = loadI32(0, STATE_COUNT);
     while (iconCount) {
         float tmpSin = sinf(rot);
         float tmpCos = cosf(rot);
+            //debugF("rot", rot);
 
         float tx1 = tmpSin * diam - (tmpCos * scale);
         float tx2 = tx1 + (tmpCos * scale * 2.f);
@@ -82,24 +87,100 @@
 
         int y;
         for (y = rowCount -1; (y >= 0) && iconCount; y--) {
-            float ty1 = ((y * 3.0f) - 4.5f) * scale;
+            float ty1 = ((y * 3.1f) - 5.f) * scale;
             float ty2 = ty1 + scale * 2.f;
-            bindTexture(NAMED_PF, 0, loadI32(1, y));
-            color(1.0f, 1.0f, 1.0f, 1.0f);
-            if (done && (index != selectedID)) {
-                color(0.4f, 0.4f, 0.4f, 1.0f);
-            }
+            bindTexture(NAMED_PF, 0, loadI32(1, index));
+            //if (done && (index != selectedID)) {
+                //color(0.4f, 0.4f, 0.4f, 1.0f);
+            //}
             drawQuad(tx1, ty1, tz1,
                      tx2, ty1, tz2,
                      tx2, ty2, tz2,
                      tx1, ty2, tz1);
+
             iconCount--;
             index++;
         }
         rot = rot + rotStep;
     }
 
-    return 0;
+    if ((zoom < 1.1f) && (zoom > 0.9f)) {
+        bindProgramVertex(NAMED_PVOrtho);
+        bindProgramFragment(NAMED_PFText);
+        bindProgramFragmentStore(NAMED_PFSText);
+
+        rot = drawRot * scale;
+        index = 0;
+        iconCount = loadI32(0, STATE_COUNT);
+        while (iconCount) {
+            int y;
+
+            float tx = 240.f + floorf(sinf(rot) * 430.f) - 64.f + 16.f;
+
+            float alpha = 2.4f - (fabsf(tx - 240.f + 48.f) / 76.f);
+            if (alpha > 0.99f) {
+                alpha = 0.99f;
+            }
+            alpha = alpha * (1.f - (fabsf(zoom - 1.f) * 10.f));
+
+            tx = tx + 0.25f;
+
+            for (y = rowCount -1; (y >= 0) && iconCount; y--) {
+
+                if (alpha > 0) {
+                    color(1.0f, 1.0f, 1.0f, alpha);
+
+                    float ty = 605.f - y * 150.f;
+
+                    ty = ty + 0.25f;
+
+                    bindTexture(NAMED_PFText, 0, loadI32(3, index));
+                    drawRect(tx, ty, tx + 128.f, ty + 32.f, 0.5f);
+                }
+                iconCount--;
+                index++;
+            }
+            rot = rot + rotStep;
+        }
+
+
+        bindProgramVertex(NAMED_PV);
+        bindProgramFragment(NAMED_PF);
+        bindProgramFragmentStore(NAMED_PFS);
+    }
+
+    // Draw the selected icon
+    color(1.0f, 1.0f, 1.0f, 0.9f);
+    rot = drawRot * scale;
+    index = 0;
+    iconCount = loadI32(0, STATE_COUNT);
+    while (iconCount) {
+        int y;
+        for (y = rowCount -1; (y >= 0) && iconCount; y--) {
+            if (index == selectedID) {
+
+                float tmpSin = sinf(rot) * scale;
+                float tmpCos = cosf(rot) * scale;
+                float tx1 = tmpSin * diam * 0.9f - tmpCos * 2.f;
+                float tx2 = tx1 + (tmpCos * 4.f);
+                float tz1 = tmpCos * diam * 0.9f + tmpSin * 2.f;
+                float tz2 = tz1 - (tmpSin * 4.f);
+
+                float ty1 = ((y * 3.1f) - 4.5f) * scale;
+                float ty2 = ty1 + scale * 4.f;
+                bindTexture(NAMED_PF, 0, loadI32(1, index));
+                drawQuad(tx1, ty1, tz1,
+                         tx2, ty1, tz2,
+                         tx2, ty2, tz2,
+                         tx1, ty2, tz1);
+            }
+            iconCount--;
+            index++;
+        }
+        rot = rot + rotStep;
+    }
+
+    return 1;
 }
 
 
diff --git a/libs/rs/java/Rollo/res/raw/rollo2.c b/libs/rs/java/Rollo/res/raw/rollo2.c
index b04ea73..256fa3c 100644
--- a/libs/rs/java/Rollo/res/raw/rollo2.c
+++ b/libs/rs/java/Rollo/res/raw/rollo2.c
@@ -3,65 +3,153 @@
 #pragma stateFragment(PF)
 #pragma stateFragmentStore(PFS)
 
-void drawLoop(int x, int y, int z, int rot)
+// Scratch buffer layout
+#define SCRATCH_FADE 0
+#define SCRATCH_ZOOM 1
+#define SCRATCH_ROT 2
+
+//#define STATE_POS_X             0
+#define STATE_DONE              1
+//#define STATE_PRESSURE          2
+#define STATE_ZOOM              3
+//#define STATE_WARP              4
+#define STATE_ORIENTATION       5
+#define STATE_SELECTION         6
+#define STATE_FIRST_VISIBLE     7
+#define STATE_COUNT             8
+#define STATE_TOUCH             9
+
+float filter(float val, float target, float str)
 {
-    int ct;
-    int tx;
-    int ty;
-    int tmpSin;
-    int tmpCos;
-    int sz;
-
-    for (ct = 0; ct < 10; ct ++) {
-        tmpSin = sinx((ct * 36 + rot) * 0x10000);
-        tmpCos = cosx((ct * 36 + rot) * 0x10000);
-
-        ty = y + tmpCos * 4;
-        tx = x + tmpSin * 4;
-        pfBindTexture(NAMED_PF, 0, loadI32(1, ct & 3));
-
-        sz = 0xc000;
-        drawQuad(tx - sz, ty - sz, z,
-                 tx + sz, ty - sz, z,
-                 tx + sz, ty + sz, z,
-                 tx - sz, ty + sz, z);
-    }
+    float delta = (target - val);
+    return val + delta * str;
 }
 
+
 int main(void* con, int ft, int launchID)
 {
     int rowCount;
-    int x;
-    int y;
-    int row;
-    int col;
     int imageID;
-    int tx1;
-    int ty1;
-    int tz1;
-    int tx2;
-    int ty2;
-    int tz2;
-    int tmpSin;
-    int tmpCos;
-    int iconCount;
-    int pressure;
+    int done = loadI32(0, STATE_DONE);
+    int selectedID = loadI32(0, STATE_SELECTION);
+    int iconCount = loadI32(0, STATE_COUNT);
 
-    int ringCount;
+    float f = loadF(2, 0);
 
+    float iconSize = 1.f;
+    float iconSpacing = 0.2f;
+    float z = 4.f;
 
-
-    rotStep = 16 * 0x10000;
-    pressure = loadI32(0, 2);
-    rowCount = 4;
-
-    iconCount = loadI32(0, 1);
-    rot = (-20 + loadI32(0, 0)) * 0x10000;
-
-    for (ringCount = 0; ringCount < 5; ringCount++) {
-        drawLoop(0, 0, 0x90000 + (ringCount * 0x80000));
+    pfClearColor(0.0f, 0.0f, 0.0f, f);
+    if (done) {
+    } else {
+        if (f < 0.8f) {
+            f = f + 0.02f;
+            storeF(2, 0, f);
+        }
     }
 
-    return 0;
+    float touchCut = 1.f;
+    if (loadI32(0, STATE_TOUCH)) {
+        touchCut = 5.f;
+    }
+
+
+    float targetZoom = ((float)loadI32(0, STATE_ZOOM)) / 1000.f;
+    float zoom = filter(loadF(2, SCRATCH_ZOOM), targetZoom, 0.15 * touchCut);
+    storeF(2, SCRATCH_ZOOM, zoom);
+
+    float targetPos = loadI32(0, STATE_FIRST_VISIBLE) / (-20.0f);
+    float pos = filter(loadF(2, SCRATCH_ROT), targetPos, 0.1f * touchCut);
+    storeF(2, SCRATCH_ROT, pos);
+    pos = pos - 1.f;
+
+    color(1.0f, 1.0f, 1.0f, 1.0f);
+
+
+    // Draw flat icons first
+    int index = ((int)pos) * 4;
+    int row;
+    int col;
+    float xoffset = -0.3f;
+    float gridSize = iconSize * 4.f + iconSpacing * 3.f;
+    float yoffset = (pos - ((int)pos));
+    for (row = 0; row < 4; row ++) {
+        float ty1 = (gridSize / 2.f) - ((float)row - yoffset) * (iconSize + iconSpacing) - iconSize;
+        float ty2 = ty1 + iconSize;
+
+        for (col = 0; (col < 4) && (index < iconCount); col ++) {
+            if (index >= 0) {
+                bindTexture(NAMED_PF, 0, loadI32(1, index));
+                float fcol = col;
+                float tx1 = xoffset + (-gridSize / 2.f) + (fcol * (iconSize + iconSpacing));
+                float tx2 = tx1 + iconSize;
+
+                drawQuad(tx1, ty1, z,
+                         tx2, ty1, z,
+                         tx2, ty2, z,
+                         tx1, ty2, z);
+            }
+            index++;
+        }
+    }
+
+    // bottom roller
+    {
+        float roll = (1.f - yoffset) * 0.5f * 3.14f;
+        float tmpSin = sinf(roll);
+        float tmpCos = cosf(roll);
+
+        for (col = 0; (col < 4) && (index < iconCount) && (index >= 0); col ++) {
+            float ty2 = (gridSize / 2.f) - ((float)row - yoffset) * (iconSize + iconSpacing);
+            float ty1 = ty2 - tmpCos * iconSize;
+
+            float tz1 = z + tmpSin * iconSize;
+            float tz2 = z;
+
+            float tx1 = xoffset + (-gridSize / 2.f) + ((float)col * (iconSize + iconSpacing));
+            float tx2 = tx1 + iconSize;
+
+            bindTexture(NAMED_PF, 0, loadI32(1, index));
+            drawQuad(tx1, ty1, tz1,
+                     tx2, ty1, tz1,
+                     tx2, ty2, tz2,
+                     tx1, ty2, tz2);
+            index++;
+        }
+    }
+
+    // Top roller
+    {
+        index = (((int)pos) * 4) - 4;
+        float roll = yoffset * 0.5f * 3.14f;
+        float tmpSin = sinf(roll);
+        float tmpCos = cosf(roll);
+
+        for (col = 0; (col < 4) && (index < iconCount) && (index >= 0); col ++) {
+            float ty1 = (gridSize / 2.f) - ((float)-1.f - yoffset) * (iconSize + iconSpacing) - iconSize;
+            float ty2 = ty1 + tmpCos * iconSize;
+
+            float tz1 = z;
+            float tz2 = z + tmpSin * iconSize;
+
+            float tx1 = xoffset + (-gridSize / 2.f) + ((float)col * (iconSize + iconSpacing));
+            float tx2 = tx1 + iconSize;
+
+            bindTexture(NAMED_PF, 0, loadI32(1, index));
+            drawQuad(tx1, ty1, tz1,
+                     tx2, ty1, tz1,
+                     tx2, ty2, tz2,
+                     tx1, ty2, tz2);
+            index++;
+        }
+    }
+
+
+
+
+    return 1;
 }
 
+
+
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
index 8f48335..520e3e4 100644
--- a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
+++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
@@ -18,6 +18,7 @@
 
 import java.io.Writer;
 
+import android.renderscript.RSSurfaceView;
 import android.renderscript.RenderScript;
 import android.renderscript.ProgramVertexAlloc;
 
@@ -25,8 +26,11 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.Typeface;
 import android.os.Handler;
 import android.os.Message;
 import android.util.AttributeSet;
@@ -56,6 +60,8 @@
     public void init(RenderScript rs, Resources res, int width, int height) {
         mRS = rs;
         mRes = res;
+        mWidth = width;
+        mHeight = height;
         initNamed();
         initRS();
     }
@@ -78,25 +84,34 @@
     }
 
     public void setSelected(int index) {
-        Log.e("rs",  "setSelected " + Integer.toString(index));
+        //Log.e("rs",  "setSelected " + Integer.toString(index));
 
         mAllocStateBuf[STATE_SELECTION] = index;
         mAllocStateBuf[STATE_DONE] = 1;
         mAllocState.data(mAllocStateBuf);
     }
 
+    private int mWidth;
+    private int mHeight;
 
     private Resources mRes;
     private RenderScript mRS;
     private RenderScript.Script mScript;
     private RenderScript.Sampler mSampler;
+    private RenderScript.Sampler mSamplerText;
     private RenderScript.ProgramFragmentStore mPFSBackground;
+    private RenderScript.ProgramFragmentStore mPFSText;
     private RenderScript.ProgramFragment mPFBackground;
     private RenderScript.ProgramFragment mPFImages;
+    private RenderScript.ProgramFragment mPFText;
     private RenderScript.ProgramVertex mPV;
     private ProgramVertexAlloc mPVAlloc;
+    private RenderScript.ProgramVertex mPVOrtho;
+    private ProgramVertexAlloc mPVOrthoAlloc;
     private RenderScript.Allocation[] mIcons;
+    private RenderScript.Allocation[] mLabels;
     private RenderScript.Allocation mIconPlate;
+    private RenderScript.Allocation mBackground;
 
     private int[] mAllocStateBuf;
     private RenderScript.Allocation mAllocState;
@@ -104,6 +119,9 @@
     private int[] mAllocIconIDBuf;
     private RenderScript.Allocation mAllocIconID;
 
+    private int[] mAllocLabelIDBuf;
+    private RenderScript.Allocation mAllocLabelID;
+
     private int[] mAllocScratchBuf;
     private RenderScript.Allocation mAllocScratch;
 
@@ -119,6 +137,17 @@
                        RenderScript.SamplerValue.CLAMP);
         mSampler = mRS.samplerCreate();
 
+        mRS.samplerBegin();
+        mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN,
+                       RenderScript.SamplerValue.NEAREST);
+        mRS.samplerSet(RenderScript.SamplerParam.FILTER_MAG,
+                       RenderScript.SamplerValue.NEAREST);
+        mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_S,
+                       RenderScript.SamplerValue.CLAMP);
+        mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_T,
+                       RenderScript.SamplerValue.CLAMP);
+        mSamplerText = mRS.samplerCreate();
+
 
         mRS.programFragmentBegin(null, null);
         mRS.programFragmentSetTexEnable(0, true);
@@ -127,26 +156,47 @@
         mPFImages.setName("PF");
         mPFImages.bindSampler(mSampler, 0);
 
+        mRS.programFragmentBegin(null, null);
+        mRS.programFragmentSetTexEnable(0, true);
+        mRS.programFragmentSetTexEnvMode(0, RenderScript.EnvMode.MODULATE);
+        mPFText = mRS.programFragmentCreate();
+        mPFText.setName("PFText");
+        mPFText.bindSampler(mSamplerText, 0);
+
         mRS.programFragmentStoreBegin(null, null);
         mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.LESS);
         mRS.programFragmentStoreDitherEnable(false);
-        mRS.programFragmentStoreDepthMask(false);
-        mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.ONE, 
-                                          RenderScript.BlendDstFunc.ONE);
+        mRS.programFragmentStoreDepthMask(true);
+        mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.SRC_ALPHA,
+                                          RenderScript.BlendDstFunc.ONE_MINUS_SRC_ALPHA);
         mPFSBackground = mRS.programFragmentStoreCreate();
         mPFSBackground.setName("PFS");
 
+        mRS.programFragmentStoreBegin(null, null);
+        mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS);
+        mRS.programFragmentStoreDitherEnable(false);
+        mRS.programFragmentStoreDepthMask(false);
+        mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.SRC_ALPHA,
+                                          RenderScript.BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+        mPFSText = mRS.programFragmentStoreCreate();
+        mPFSText.setName("PFSText");
+
         mPVAlloc = new ProgramVertexAlloc(mRS);
         mRS.programVertexBegin(null, null);
-        mRS.programVertexSetTextureMatrixEnable(true);
+        mRS.programVertexSetTextureMatrixEnable(false);
         mPV = mRS.programVertexCreate();
         mPV.setName("PV");
         mPV.bindAllocation(0, mPVAlloc.mAlloc);
+        mPVAlloc.setupProjectionNormalized(mWidth, mHeight);
 
+        mPVOrthoAlloc = new ProgramVertexAlloc(mRS);
+        mRS.programVertexBegin(null, null);
+        mRS.programVertexSetTextureMatrixEnable(true);
+        mPVOrtho = mRS.programVertexCreate();
+        mPVOrtho.setName("PVOrtho");
+        mPVOrtho.bindAllocation(0, mPVOrthoAlloc.mAlloc);
+        mPVOrthoAlloc.setupOrthoWindow(mWidth, mHeight);
 
-
-        mPVAlloc.setupProjectionNormalized(320, 480);
-        //mPVAlloc.setupOrthoNormalized(320, 480);
         mRS.contextBindProgramVertex(mPV);
 
         mAllocScratchBuf = new int[32];
@@ -162,33 +212,175 @@
 
 
         {
-            mIcons = new RenderScript.Allocation[4];
+            mIcons = new RenderScript.Allocation[29];
             mAllocIconIDBuf = new int[mIcons.length];
             mAllocIconID = mRS.allocationCreatePredefSized(
                 RenderScript.ElementPredefined.USER_I32, mAllocIconIDBuf.length);
 
-            
+            mLabels = new RenderScript.Allocation[29];
+            mAllocLabelIDBuf = new int[mLabels.length];
+            mAllocLabelID = mRS.allocationCreatePredefSized(
+                RenderScript.ElementPredefined.USER_I32, mLabels.length);
+
+
             Bitmap b;
             BitmapFactory.Options opts = new BitmapFactory.Options();
             opts.inScaled = false;
 
+            b = BitmapFactory.decodeResource(mRes, R.raw.cf_background, opts);
+            mBackground = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mBackground.setName("TexBk");
+
+
             b = BitmapFactory.decodeResource(mRes, R.raw.browser, opts);
-            mIcons[0] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mIcons[0] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGBA_8888, true);
+            mLabels[0] = makeTextBitmap("browser");
 
             b = BitmapFactory.decodeResource(mRes, R.raw.market, opts);
-            mIcons[1] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mIcons[1] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGBA_8888, true);
+            mLabels[1] = makeTextBitmap("market");
 
             b = BitmapFactory.decodeResource(mRes, R.raw.photos, opts);
-            mIcons[2] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mIcons[2] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGBA_8888, true);
+            mLabels[2] = makeTextBitmap("photos");
 
             b = BitmapFactory.decodeResource(mRes, R.raw.settings, opts);
-            mIcons[3] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mIcons[3] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGBA_8888, true);
+            mLabels[3] = makeTextBitmap("settings");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.calendar, opts);
+            mIcons[4] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[4] = makeTextBitmap("creed");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.g1155, opts);
+            mIcons[5] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[5] = makeTextBitmap("BOA");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.g2140, opts);
+            mIcons[6] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[6] = makeTextBitmap("chess");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.maps, opts);
+            mIcons[7] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[7] = makeTextBitmap("Dictionary");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.path431, opts);
+            mIcons[8] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[8] = makeTextBitmap("facebook");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.path676, opts);
+            mIcons[9] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[9] = makeTextBitmap("Flash Light");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.path754, opts);
+            mIcons[10] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[10] = makeTextBitmap("Flight Control");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.path815, opts);
+            mIcons[11] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[11] = makeTextBitmap("google earth");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.path1920, opts);
+            mIcons[12] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[12] = makeTextBitmap("Harry Potter");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.path1927, opts);
+            mIcons[13] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[13] = makeTextBitmap("Movies");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.path3099, opts);
+            mIcons[14] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[14] = makeTextBitmap("NY Times");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.path3950, opts);
+            mIcons[15] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[15] = makeTextBitmap("Pandora");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.path4481, opts);
+            mIcons[16] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[16] = makeTextBitmap("Public Radio");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.path5168, opts);
+            mIcons[17] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[17] = makeTextBitmap("Public Radio");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.polygon2408, opts);
+            mIcons[18] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[18] = makeTextBitmap("Public Radio");
+
+            /*
+            b = BitmapFactory.decodeResource(mRes, R.raw.solitaire, opts);
+            mIcons[19] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[19] = makeTextBitmap("Public Radio");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.sudoku, opts);
+            mIcons[20] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[20] = makeTextBitmap("Public Radio");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.taptaprevenge, opts);
+            mIcons[21] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[21] = makeTextBitmap("Public Radio");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.tetris, opts);
+            mIcons[22] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[22] = makeTextBitmap("Public Radio");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.tictactoe, opts);
+            mIcons[23] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[23] = makeTextBitmap("Public Radio");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.tweetie, opts);
+            mIcons[24] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[24] = makeTextBitmap("Public Radio");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.urbanspoon, opts);
+            mIcons[25] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[25] = makeTextBitmap("Public Radio");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.waterslide_extreme, opts);
+            mIcons[26] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[26] = makeTextBitmap("Public Radio");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.weather_channel, opts);
+            mIcons[27] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[27] = makeTextBitmap("Public Radio");
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.zippo, opts);
+            mIcons[28] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mLabels[28] = makeTextBitmap("Public Radio");
+*/
+
+            mIcons[19] = mIcons[0];
+            mIcons[20] = mIcons[1];
+            mIcons[21] = mIcons[2];
+            mIcons[22] = mIcons[3];
+            mIcons[23] = mIcons[2];
+            mIcons[24] = mIcons[1];
+            mIcons[25] = mIcons[0];
+            mIcons[26] = mIcons[1];
+            mIcons[27] = mIcons[2];
+            mIcons[28] = mIcons[3];
+
+            mLabels[19] = mLabels[0];
+            mLabels[20] = mLabels[1];
+            mLabels[21] = mLabels[2];
+            mLabels[22] = mLabels[3];
+            mLabels[23] = mLabels[2];
+            mLabels[24] = mLabels[1];
+            mLabels[25] = mLabels[0];
+            mLabels[26] = mLabels[1];
+            mLabels[27] = mLabels[2];
+            mLabels[28] = mLabels[3];
+
 
             for(int ct=0; ct < mIcons.length; ct++) {
                 mIcons[ct].uploadToTexture(0);
+                mLabels[ct].uploadToTexture(0);
                 mAllocIconIDBuf[ct] = mIcons[ct].getID();
+                mAllocLabelIDBuf[ct] = mLabels[ct].getID();
             }
             mAllocIconID.data(mAllocIconIDBuf);
+            mAllocLabelID.data(mAllocLabelIDBuf);
 
             RenderScript.Element e = mRS.elementGetPredefined(RenderScript.ElementPredefined.RGB_565);
             mRS.typeBegin(e);
@@ -221,6 +413,16 @@
 
     }
 
+    RenderScript.Allocation makeTextBitmap(String t) {
+        Bitmap b = Bitmap.createBitmap(128, 32, Bitmap.Config.ARGB_8888);
+        Canvas c = new Canvas(b);
+        Paint p = new Paint();
+        p.setTypeface(Typeface.DEFAULT_BOLD);
+        p.setTextSize(16);
+        p.setColor(0xffffffff);
+        c.drawText(t, 2, 20, p);
+        return mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGBA_8888, true);
+    }
 
 
     private void initRS() {
@@ -232,12 +434,13 @@
         //mRS.scriptCSetClearDepth(0);
         mScript = mRS.scriptCCreate();
 
-        mAllocStateBuf = new int[] {0, 0, 0, 8, 0, 0, 0, 0, 38, 0, 0};
+        mAllocStateBuf = new int[] {0, 0, 0, 8, 0, 0, -1, 0, mAllocIconIDBuf.length, 0, 0};
         mAllocState = mRS.allocationCreatePredefSized(
             RenderScript.ElementPredefined.USER_I32, mAllocStateBuf.length);
         mScript.bindAllocation(mAllocState, 0);
         mScript.bindAllocation(mAllocIconID, 1);
         mScript.bindAllocation(mAllocScratch, 2);
+        mScript.bindAllocation(mAllocLabelID, 3);
         setPosition(0);
         setZoom(1);
 
@@ -248,4 +451,3 @@
 }
 
 
-
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
index b5e02af..71d6c7e 100644
--- a/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
+++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
@@ -76,7 +76,7 @@
     float mOldColumn;
     float mZoom = 1;
 
-    int mIconCount = 38;
+    int mIconCount = 29;
     int mRows = 4;
     int mColumns = (mIconCount + mRows - 1) / mRows;
 
@@ -90,8 +90,8 @@
         if(c > (mColumns -2)) {
             c = (mColumns -2);
         }
-        if(c < 1) {
-            c = 1;
+        if(c < 0) {
+            c = 0;
         }
         mRender.setPosition(c);
         if(clamp) {
@@ -101,11 +101,11 @@
 
     void computeSelection(float x, float y)
     {
-        float col = mColumn + (x - 0.5f) * 3;
+        float col = mColumn + (x - 0.5f) * 4 + 1.25f;
         int iCol = (int)(col + 0.25f);
 
         float row = (y / 0.8f) * mRows;
-        int iRow = (int)(row - 0.25f);
+        int iRow = (int)(row - 0.5f);
 
         mRender.setSelected(iCol * mRows + iRow);
     }
@@ -122,6 +122,9 @@
         float nx = ev.getX() / getWidth();
         float ny = ev.getY() / getHeight();
 
+        //Log.e("rs", "width=" + Float.toString(getWidth()));
+        //Log.e("rs", "height=" + Float.toString(getHeight()));
+
         mRender.setTouch(ret);
 
         if((ny > 0.85f) || mControlMode) {
@@ -158,13 +161,14 @@
                     mZoom = zoom;
                     mFlingX = nx;
                     mRender.setZoom(zoom);
-                } else {
-                    if(mControlMode && (mZoom < 1.01f)) {
+                    if(mZoom < 1.01f) {
                         computeSelection(nx, ny);
                     }
+                } else {
                     mControlMode = false;
                     mColumn = mOldColumn;
                     mRender.setZoom(1.f);
+                    mRender.setSelected(-1);
                 }
             } else {
                 // Do something with corners here....
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 2f99808..45e6d1b 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -99,6 +99,16 @@
 	ret RsAllocation
 	}
 
+AllocationCreateFromBitmapBoxed {
+	param uint32_t width
+	param uint32_t height
+	param RsElementPredefined dstFmt
+	param RsElementPredefined srcFmt
+	param bool genMips
+	param const void * data
+	ret RsAllocation
+	}
+
 
 AllocationUploadToTexture {
 	param RsAllocation alloc
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index c143307..c6a9149 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -204,12 +204,12 @@
     uint32_t w = out.getDimX();
     uint32_t h = out.getDimY();
 
-    for (uint32_t y=0; y < w; y++) {
+    for (uint32_t y=0; y < h; y++) {
         uint16_t *oPtr = static_cast<uint16_t *>(out.getElement(0, y));
         const uint16_t *i1 = static_cast<uint16_t *>(in.getElement(0, y*2));
         const uint16_t *i2 = static_cast<uint16_t *>(in.getElement(0, y*2+1));
 
-        for (uint32_t x=0; x < h; x++) {
+        for (uint32_t x=0; x < w; x++) {
             *oPtr = rsBoxFilter565(i1[0], i1[1], i2[0], i2[1]);
             oPtr ++;
             i1 += 2;
@@ -223,21 +223,33 @@
     uint32_t w = out.getDimX();
     uint32_t h = out.getDimY();
 
-    for (uint32_t y=0; y < w; y++) {
+    for (uint32_t y=0; y < h; 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++) {
+        for (uint32_t x=0; x < w; x++) {
             *oPtr = rsBoxFilter8888(i1[0], i1[1], i2[0], i2[1]);
             oPtr ++;
             i1 += 2;
             i2 += 2;
         }
     }
-
 }
 
+static void mip(const Adapter2D &out, const Adapter2D &in)
+{
+    switch(out.getBaseType()->getElement()->getSizeBits()) {
+    case 32:
+        mip8888(out, in);
+        break;
+    case 16:
+        mip565(out, in);
+        break;
+
+    }
+
+}
 
 typedef void (*ElementConverter_t)(void *dst, const void *src, uint32_t count);
 
@@ -301,14 +313,18 @@
         return elementConverter_cpy_32;
     }
 
-    LOGE("pickConverter, unsuported combo");
+    LOGE("pickConverter, unsuported combo, src %i,  dst %i", srcFmt, dstFmt);
     return 0;
 }
 
 
 RsAllocation rsi_AllocationCreateFromBitmap(Context *rsc, uint32_t w, uint32_t h, RsElementPredefined dstFmt, RsElementPredefined srcFmt,  bool genMips, const void *data)
 {
-    rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGB_565));
+    rsAssert(!(w & (w-1)));
+    rsAssert(!(h & (h-1)));
+
+    //LOGE("rsi_AllocationCreateFromBitmap %i %i %i %i %i", w, h, dstFmt, srcFmt, genMips);
+    rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, dstFmt));
     rsi_TypeAdd(rsc, RS_DIMENSION_X, w);
     rsi_TypeAdd(rsc, RS_DIMENSION_Y, h);
     if (genMips) {
@@ -333,13 +349,49 @@
         for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) {
             adapt.setLOD(lod);
             adapt2.setLOD(lod + 1);
-            mip565(adapt2, adapt);
+            mip(adapt2, adapt);
         }
     }
 
     return texAlloc;
 }
 
+static uint32_t fmtToBits(RsElementPredefined fmt)
+{
+    return 16;
+}
+
+RsAllocation rsi_AllocationCreateFromBitmapBoxed(Context *rsc, uint32_t w, uint32_t h, RsElementPredefined dstFmt, RsElementPredefined srcFmt, bool genMips, const void *data)
+{
+    uint32_t w2 = rsHigherPow2(w);
+    uint32_t h2 = rsHigherPow2(h);
+
+    if ((w2 == w) && (h2 == h)) {
+        return rsi_AllocationCreateFromBitmap(rsc, w, h, dstFmt, srcFmt, genMips, data);
+    }
+
+    uint32_t bpp = fmtToBits(srcFmt) >> 3;
+    size_t size = w2 * h2 * bpp;
+    uint8_t *tmp = static_cast<uint8_t *>(malloc(size));
+    memset(tmp, 0, size);
+
+    const uint8_t * src = static_cast<const uint8_t *>(data);
+    for (uint32_t y = 0; y < h; y++) {
+        uint8_t * ydst = &tmp[y + ((h2 - h) >> 1)];
+        memcpy(&ydst[(w2 - w) >> 1], src, w * bpp);
+        src += h * bpp;
+    }
+
+    RsAllocation ret = rsi_AllocationCreateFromBitmap(rsc, w2, h2, dstFmt, srcFmt, genMips, tmp);
+    free(tmp);
+    return ret;
+
+
+
+
+}
+
+
 RsAllocation rsi_AllocationCreateFromFile(Context *rsc, const char *file, bool genMips)
 {
     bool use32bpp = false;
@@ -442,11 +494,7 @@
         for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) {
             adapt.setLOD(lod);
             adapt2.setLOD(lod + 1);
-            if (use32bpp) {
-                mip8888(adapt2, adapt);
-            } else {
-                mip565(adapt2, adapt);
-            }
+            mip(adapt2, adapt);
         }
     }
 
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 78b8bf8..e52b0e0 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -79,8 +79,7 @@
     mFragment.set(frag);
     mVertex.set(vtx);
     mFragmentStore.set(store);
-    return true;
-
+    return ret;
 }
 
 
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 497dbcf..a00b8e8 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -83,6 +83,7 @@
 
     const ProgramFragment * getFragment() {return mFragment.get();}
     const ProgramFragmentStore * getFragmentStore() {return mFragmentStore.get();}
+    const ProgramVertex * getVertex() {return mVertex.get();}
 
     void setupCheck();
 
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index 417ba6a..792135d 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -55,8 +55,6 @@
         glLoadIdentity();
     }
 
-
-    LOGE("lights %i ", mLightCount);
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
     if (mLightCount) {
@@ -103,6 +101,25 @@
     }
 }
 
+void ProgramVertex::setProjectionMatrix(const rsc_Matrix *m) const
+{
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
+    memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m, sizeof(rsc_Matrix));
+}
+
+void ProgramVertex::setModelviewMatrix(const rsc_Matrix *m) const
+{
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
+    memcpy(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], m, sizeof(rsc_Matrix));
+}
+
+void ProgramVertex::setTextureMatrix(const rsc_Matrix *m) const
+{
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
+    memcpy(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET], m, sizeof(rsc_Matrix));
+}
+
+
 
 ProgramVertexState::ProgramVertexState()
 {
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index ac15b70..da5ed81 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -41,6 +41,10 @@
     void setTextureMatrixEnable(bool e) {mTextureMatrixEnable = e;}
     void addLight(const Light *);
 
+    void setProjectionMatrix(const rsc_Matrix *) const;
+    void setModelviewMatrix(const rsc_Matrix *) const;
+    void setTextureMatrix(const rsc_Matrix *) const;
+
 protected:
     bool mDirty;
     uint32_t mLightCount;
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 129b19f..41219064 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -135,7 +135,6 @@
 
 
 
-
 //////////////////////////////////////////////////////////////////////////////
 // Matrix routines
 //////////////////////////////////////////////////////////////////////////////
@@ -257,6 +256,24 @@
 }
 
 //////////////////////////////////////////////////////////////////////////////
+// VP
+//////////////////////////////////////////////////////////////////////////////
+
+static void SC_vpLoadModelMatrix(const rsc_Matrix *m)
+{
+    GET_TLS();
+    rsc->getVertex()->setModelviewMatrix(m);
+}
+
+static void SC_vpLoadTextureMatrix(const rsc_Matrix *m)
+{
+    GET_TLS();
+    rsc->getVertex()->setTextureMatrix(m);
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////////
 // Drawing
 //////////////////////////////////////////////////////////////////////////////
 
@@ -339,24 +356,25 @@
     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 }
 
+static void SC_drawRect(float x1, float y1,
+                        float x2, float y2, float z)
+{
+    SC_drawQuad(x1, y2, z,
+                x2, y2, z,
+                x2, y1, z,
+                x1, y1, z);
+}
+
 //////////////////////////////////////////////////////////////////////////////
 //
 //////////////////////////////////////////////////////////////////////////////
 
-extern "C" const void * loadVp(uint32_t bank, uint32_t offset)
-{
-    GET_TLS();
-    return &static_cast<const uint8_t *>(sc->mSlots[bank]->getPtr())[offset];
-}
-
-
-
 static void SC_color(float r, float g, float b, float a)
 {
     glColor4f(r, g, b, a);
 }
 
-
+/*
 extern "C" void materialDiffuse(float r, float g, float b, float a)
 {
     float v[] = {r, g, b, a};
@@ -369,35 +387,18 @@
     glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, v);
 }
 
-extern "C" void lightPosition(float x, float y, float z, float w)
-{
-    float v[] = {x, y, z, w};
-    glLightfv(GL_LIGHT0, GL_POSITION, v);
-}
-
 extern "C" void materialShininess(float s)
 {
     glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &s);
 }
+*/
 
-extern "C" void uploadToTexture(RsAllocation va, uint32_t baseMipLevel)
+static void SC_uploadToTexture(RsAllocation va, uint32_t baseMipLevel)
 {
     GET_TLS();
     rsi_AllocationUploadToTexture(rsc, va, baseMipLevel);
 }
 
-extern "C" void enable(uint32_t p)
-{
-    glEnable(p);
-}
-
-extern "C" void disable(uint32_t p)
-{
-    glDisable(p);
-}
-
-
-
 static void SC_ClearColor(float r, float g, float b, float a)
 {
     //LOGE("c %f %f %f %f", r, g, b, a);
@@ -408,6 +409,16 @@
     sc->mEnviroment.mClearColor[3] = a;
 }
 
+static void SC_debugF(const char *s, float f)
+{
+    LOGE("%s %f", s, f);
+}
+
+static void SC_debugI32(const char *s, int32_t i)
+{
+    LOGE("%s %i", s, i);
+}
+
 
 
 //////////////////////////////////////////////////////////////////////////////
@@ -440,10 +451,14 @@
         "float", "(float)" },
     { "cosf", (void *)&cosf,
         "float", "(float)" },
-    { "fabs", (void *)&fabs,
+    { "fabsf", (void *)&fabsf,
         "float", "(float)" },
     { "randf", (void *)&SC_randf,
         "float", "(float)" },
+    { "floorf", (void *)&floorf,
+        "float", "(float)" },
+    { "ceilf", (void *)&ceilf,
+        "float", "(float)" },
 
     // matrix
     { "matrixLoadIdentity", (void *)&SC_matrixLoadIdentity,
@@ -481,7 +496,17 @@
     { "bindTexture", (void *)&SC_bindTexture,
         "void", "(int, int, int)" },
 
+    // vp
+    { "vpLoadModelMatrix", (void *)&SC_bindProgramFragment,
+        "void", "(void *)" },
+    { "vpLoadTextureMatrix", (void *)&SC_bindProgramFragmentStore,
+        "void", "(void *)" },
+
+
+
     // drawing
+    { "drawRect", (void *)&SC_drawRect,
+        "void", "(float x1, float y1, float x2, float y2, float z)" },
     { "drawQuad", (void *)&SC_drawQuad,
         "void", "(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4)" },
     { "drawTriangleArray", (void *)&SC_drawTriangleArray,
@@ -495,10 +520,19 @@
     // misc
     { "pfClearColor", (void *)&SC_ClearColor,
         "void", "(float, float, float, float)" },
-
     { "color", (void *)&SC_color,
         "void", "(float, float, float, float)" },
 
+    { "uploadToTexture", (void *)&SC_uploadToTexture,
+        "void", "(int, int)" },
+
+
+    { "debugF", (void *)&SC_debugF,
+        "void", "(void *, float)" },
+    { "debugI32", (void *)&SC_debugI32,
+        "void", "(void *, int)" },
+
+
     { NULL, NULL, NULL, NULL }
 };
 
diff --git a/libs/surfaceflinger/LayerBitmap.cpp b/libs/surfaceflinger/LayerBitmap.cpp
index 9fffbbf..ff49c87 100644
--- a/libs/surfaceflinger/LayerBitmap.cpp
+++ b/libs/surfaceflinger/LayerBitmap.cpp
@@ -177,22 +177,17 @@
 sp<Buffer> LayerBitmap::allocate()
 {
     Mutex::Autolock _l(mLock);
-    sp<Buffer> buffer(mBuffer);
-    const uint32_t w = mWidth; 
-    const uint32_t h = mHeight;
-    if (buffer!=0 && (w != buffer->getWidth() || h != buffer->getHeight())) {
-        surface_info_t* info = mInfo;
-        buffer = new Buffer(w, h, mFormat, mFlags);
-        status_t err = buffer->initCheck();
-        if (LIKELY(err == NO_ERROR)) {
-            info->flags  = surface_info_t::eBufferDirty;
-            info->status = NO_ERROR;
-        } else {
-            memset(info, 0, sizeof(surface_info_t));
-            info->status = NO_MEMORY;
-        }
-        mBuffer = buffer;
+    surface_info_t* info = mInfo;
+    sp<Buffer> buffer = new Buffer(mWidth, mHeight, mFormat, mFlags);
+    status_t err = buffer->initCheck();
+    if (LIKELY(err == NO_ERROR)) {
+        info->flags  = surface_info_t::eBufferDirty;
+        info->status = NO_ERROR;
+    } else {
+        memset(info, 0, sizeof(surface_info_t));
+        info->status = NO_MEMORY;
     }
+    mBuffer = buffer;
     return buffer;
 }
 
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 7a7574f..102899c 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -650,6 +650,7 @@
                 if (currentLayers.indexOf( layer ) < 0) {
                     // this layer is not visible anymore
                     ditchedLayers.add(layer);
+                    mDirtyRegionRemovedLayer.orSelf(layer->visibleRegionScreen);
                 }
             }
         }
@@ -685,17 +686,15 @@
         layer->validateVisibility(planeTransform);
 
         // start with the whole surface at its current location
-        const Layer::State& s = layer->drawingState();
-        const Rect bounds(layer->visibleBounds());
+        const Layer::State& s(layer->drawingState());
 
         // handle hidden surfaces by setting the visible region to empty
         Region opaqueRegion;
         Region visibleRegion;
         Region coveredRegion;
-        if (UNLIKELY((s.flags & ISurfaceComposer::eLayerHidden) || !s.alpha)) {
-            visibleRegion.clear();
-        } else {
+        if (LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)) {
             const bool translucent = layer->needsBlending();
+            const Rect bounds(layer->visibleBounds());
             visibleRegion.set(bounds);
             coveredRegion = visibleRegion;
 
@@ -742,12 +741,16 @@
         layer->setVisibleRegion(visibleRegion);
         layer->setCoveredRegion(coveredRegion);
 
-        // If a secure layer is partially visible, lock down the screen!
+        // If a secure layer is partially visible, lock-down the screen!
         if (layer->isSecure() && !visibleRegion.isEmpty()) {
             secureFrameBuffer = true;
         }
     }
 
+    // invalidate the areas where a layer was removed
+    dirtyRegion.orSelf(mDirtyRegionRemovedLayer);
+    mDirtyRegionRemovedLayer.clear();
+
     mSecureFrameBuffer = secureFrameBuffer;
     opaqueRegion = aboveOpaqueLayers;
 }
diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h
index e8687a7..2569a0f 100644
--- a/libs/surfaceflinger/SurfaceFlinger.h
+++ b/libs/surfaceflinger/SurfaceFlinger.h
@@ -334,6 +334,7 @@
                 // Can only accessed from the main thread, these members
                 // don't need synchronization
                 Region                      mDirtyRegion;
+                Region                      mDirtyRegionRemovedLayer;
                 Region                      mInvalidRegion;
                 Region                      mWormholeRegion;
                 wp<Client>                  mLastScheduledBroadcast;
diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp
index be04777..c51d989 100644
--- a/libs/utils/BackupData.cpp
+++ b/libs/utils/BackupData.cpp
@@ -196,6 +196,7 @@
             } else { \
                 m_status = errno; \
             } \
+            LOGD("CHECK_SIZE failed with at line %d m_status='%s'", __LINE__, strerror(m_status)); \
             return m_status; \
         } \
     } while(0)
@@ -203,6 +204,7 @@
     do { \
         status_t err = skip_padding(); \
         if (err != NO_ERROR) { \
+            LOGD("SKIP_PADDING FAILED at line %d", __LINE__); \
             m_status = err; \
             return err; \
         } \
@@ -218,10 +220,19 @@
 
     int amt;
 
-    // No error checking here, in case we're at the end of the stream.  Just let read() fail.
-    skip_padding();
+    amt = skip_padding();
+    if (amt == EIO) {
+        *done = true;
+        return NO_ERROR;
+    }
+    else if (amt != NO_ERROR) {
+        return amt;
+    }
     amt = read(m_fd, &m_header, sizeof(m_header));
     *done = m_done = (amt == 0);
+    if (*done) {
+        return NO_ERROR;
+    }
     CHECK_SIZE(amt, sizeof(m_header));
     m_pos += sizeof(m_header);
     if (type) {
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 9e1a72c..aaac192 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -197,6 +197,10 @@
     // properties loaded from PROPERTIES_FILE
     private Properties mProperties;
     private String mNtpServer;
+    private String mSuplServerHost;
+    private int mSuplServerPort;
+    private String mC2KServerHost;
+    private int mC2KServerPort;
 
     private final Context mContext;
     private final ILocationManager mLocationManager;
@@ -348,23 +352,21 @@
             stream.close();
             mNtpServer = mProperties.getProperty("NTP_SERVER", null);
 
-            String host = mProperties.getProperty("SUPL_HOST");
+            mSuplServerHost = mProperties.getProperty("SUPL_HOST");
             String portString = mProperties.getProperty("SUPL_PORT");
-            if (host != null && portString != null) {
+            if (mSuplServerHost != null && portString != null) {
                 try {
-                    int port = Integer.parseInt(portString);
-                    native_set_agps_server(AGPS_TYPE_SUPL, host, port);
+                    mSuplServerPort = Integer.parseInt(portString);
                 } catch (NumberFormatException e) {
                     Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
                 }
             }
 
-            host = mProperties.getProperty("C2K_HOST");
+            mC2KServerHost = mProperties.getProperty("C2K_HOST");
             portString = mProperties.getProperty("C2K_PORT");
-            if (host != null && portString != null) {
+            if (mC2KServerHost != null && portString != null) {
                 try {
-                    int port = Integer.parseInt(portString);
-                    native_set_agps_server(AGPS_TYPE_C2K, host, port);
+                    mC2KServerPort = Integer.parseInt(portString);
                 } catch (NumberFormatException e) {
                     Log.e(TAG, "unable to parse C2K_PORT: " + portString);
                 }
@@ -494,6 +496,13 @@
         mEnabled = native_init();
 
         if (mEnabled) {
+            if (mSuplServerHost != null) {
+                native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
+            }
+            if (mC2KServerHost != null) {
+                native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
+            }
+
             // run event listener thread while we are enabled
             mEventThread = new GpsEventThread();
             mEventThread.start();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 040d4bc..60fc0e0 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -649,10 +649,11 @@
      *           <var>false</var> to turn it off
      */
     public void setSpeakerphoneOn(boolean on){
-        if (on) {
-            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER);
-        } else {
-            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
+        IAudioService service = getService();
+        try {
+            service.setSpeakerphoneOn(on);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in setSpeakerphoneOn", e);
         }
     }
 
@@ -662,9 +663,11 @@
      * @return true if speakerphone is on, false if it's off
      */
     public boolean isSpeakerphoneOn() {
-        if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) == AudioSystem.FORCE_SPEAKER) {
-            return true;
-        } else {
+        IAudioService service = getService();
+        try {
+            return service.isSpeakerphoneOn();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in isSpeakerphoneOn", e);
             return false;
         }
      }
@@ -676,10 +679,11 @@
      *               <var>false</var> to not use bluetooth SCO for communications
      */
     public void setBluetoothScoOn(boolean on){
-        if (on) {
-            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO);
-        } else {
-            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
+        IAudioService service = getService();
+        try {
+            service.setBluetoothScoOn(on);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in setBluetoothScoOn", e);
         }
     }
 
@@ -690,9 +694,11 @@
      *         false if otherwise
      */
     public boolean isBluetoothScoOn() {
-        if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) == AudioSystem.FORCE_BT_SCO) {
-            return true;
-        } else {
+        IAudioService service = getService();
+        try {
+            return service.isBluetoothScoOn();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in isBluetoothScoOn", e);
             return false;
         }
     }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 30640c3..1f9e3af 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -47,7 +47,10 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
-
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * The implementation of the volume manager service.
@@ -210,6 +213,12 @@
 
     private int mHeadsetState;
 
+    // Devices currently connected
+    private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
+
+    // Forced device usage for communications
+    private int mForcedUseForComm;
+
     ///////////////////////////////////////////////////////////////////////////
     // Construction
     ///////////////////////////////////////////////////////////////////////////
@@ -220,7 +229,9 @@
         mContentResolver = context.getContentResolver();
         mVolumePanel = new VolumePanel(context, this);
         mSettingsObserver = new SettingsObserver();
-
+        mMode = AudioSystem.MODE_NORMAL;
+        mHeadsetState = 0;
+        mForcedUseForComm = AudioSystem.FORCE_NONE;
         createAudioSystemThread();
         readPersistedSettings();
         createStreamStates();
@@ -721,6 +732,46 @@
         setRingerModeInt(getRingerMode(), false);
     }
 
+    /** @see AudioManager#setSpeakerphoneOn() */
+    public void setSpeakerphoneOn(boolean on){
+        if (on) {
+            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER);
+            mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
+        } else {
+            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
+            mForcedUseForComm = AudioSystem.FORCE_NONE;
+        }
+    }
+
+    /** @see AudioManager#isSpeakerphoneOn() */
+    public boolean isSpeakerphoneOn() {
+        if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /** @see AudioManager#setBluetoothScoOn() */
+    public void setBluetoothScoOn(boolean on){
+        if (on) {
+            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO);
+            mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
+        } else {
+            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
+            mForcedUseForComm = AudioSystem.FORCE_NONE;
+        }
+    }
+
+    /** @see AudioManager#isBluetoothScoOn() */
+    public boolean isBluetoothScoOn() {
+        if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Internal methods
     ///////////////////////////////////////////////////////////////////////////
@@ -1188,16 +1239,33 @@
                     Log.e(TAG, "Media server died.");
                     // Force creation of new IAudioflinger interface
                     mMediaServerOk = false;
-                    AudioSystem.getMode();
+                    AudioSystem.isMusicActive();
                     break;
 
                 case MSG_MEDIA_SERVER_STARTED:
                     Log.e(TAG, "Media server started.");
+                    // Restore device connection states
+                    Set set = mConnectedDevices.entrySet();
+                    Iterator i = set.iterator();
+                    while(i.hasNext()){
+                        Map.Entry device = (Map.Entry)i.next();
+                        AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(),
+                                                             AudioSystem.DEVICE_STATE_AVAILABLE,
+                                                             (String)device.getValue());
+                    }
+
+                    // Restore call state
+                    AudioSystem.setPhoneState(mMode);
+
+                    // Restore forced usage for communcations
+                    AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
+
                     // Restore stream volumes
                     int numStreamTypes = AudioSystem.getNumStreamTypes();
                     for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
                         int index;
                         VolumeStreamState streamState = mStreamStates[streamType];
+                        AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
                         if (streamState.muteCount() == 0) {
                             index = streamState.mIndex;
                         } else {
@@ -1205,7 +1273,10 @@
                         }
                         setStreamVolumeIndex(streamType, index);
                     }
-                    setRingerMode(mRingerMode);
+
+                    // Restore ringer mode
+                    setRingerModeInt(getRingerMode(), false);
+
                     mMediaServerOk = true;
                     break;
 
@@ -1276,10 +1347,12 @@
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
                             address);
+                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
                 } else if (state == BluetoothA2dp.STATE_CONNECTED){
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                                                          AudioSystem.DEVICE_STATE_AVAILABLE,
                                                          address);
+                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP), address);
                 }
             } else if (action.equals(BluetoothIntent.HEADSET_AUDIO_STATE_CHANGED_ACTION)) {
                 int state = intent.getIntExtra(BluetoothIntent.HEADSET_AUDIO_STATE,
@@ -1289,10 +1362,12 @@
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
                                                          AudioSystem.DEVICE_STATE_UNAVAILABLE,
                                                          address);
+                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO);
                 } else if (state == BluetoothHeadset.AUDIO_STATE_CONNECTED) {
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
                                                          AudioSystem.DEVICE_STATE_AVAILABLE,
                                                          address);
+                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO), address);
                 }
             } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
                 int state = intent.getIntExtra("state", 0);
@@ -1301,55 +1376,65 @@
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
                             "");
+                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
                 } else if ((state & BIT_HEADSET) != 0 &&
                     (mHeadsetState & BIT_HEADSET) == 0)  {
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
                             AudioSystem.DEVICE_STATE_AVAILABLE,
                             "");
+                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
                 }
                 if ((state & BIT_HEADSET_NO_MIC) == 0 &&
                     (mHeadsetState & BIT_HEADSET_NO_MIC) != 0) {
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
                             "");
+                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
                 } else if ((state & BIT_HEADSET_NO_MIC) != 0 &&
                     (mHeadsetState & BIT_HEADSET_NO_MIC) == 0)  {
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
                             AudioSystem.DEVICE_STATE_AVAILABLE,
                             "");
+                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
                 }
                 if ((state & BIT_TTY) == 0 &&
                     (mHeadsetState & BIT_TTY) != 0) {
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_TTY,
                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
                             "");
+                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_TTY);
                 } else if ((state & BIT_TTY) != 0 &&
                     (mHeadsetState & BIT_TTY) == 0)  {
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_TTY,
                             AudioSystem.DEVICE_STATE_AVAILABLE,
                             "");
+                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_TTY), "");
                 }
                 if ((state & BIT_FM_HEADSET) == 0 &&
                     (mHeadsetState & BIT_FM_HEADSET) != 0) {
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_HEADPHONE,
                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
                             "");
+                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_FM_HEADPHONE);
                 } else if ((state & BIT_FM_HEADSET) != 0 &&
                     (mHeadsetState & BIT_FM_HEADSET) == 0)  {
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_HEADPHONE,
                             AudioSystem.DEVICE_STATE_AVAILABLE,
                             "");
+                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_FM_HEADPHONE), "");
                 }
                 if ((state & BIT_FM_SPEAKER) == 0 &&
                     (mHeadsetState & BIT_FM_SPEAKER) != 0) {
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_SPEAKER,
                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
                             "");
+                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_FM_SPEAKER);
                 } else if ((state & BIT_FM_SPEAKER) != 0 &&
                     (mHeadsetState & BIT_FM_SPEAKER) == 0)  {
                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_SPEAKER,
                             AudioSystem.DEVICE_STATE_AVAILABLE,
                             "");
+                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_FM_SPEAKER), "");
                 }
                 mHeadsetState = state;
             }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index bb4252b..d3d2d29 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -60,4 +60,12 @@
     oneway void unloadSoundEffects();
 
     oneway void reloadAudioSettings();
+
+    void setSpeakerphoneOn(boolean on);
+
+    boolean isSpeakerphoneOn();
+
+    void setBluetoothScoOn(boolean on);
+
+    boolean isBluetoothScoOn();
 }
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 44026e4..8481410 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -605,21 +605,6 @@
             Log.e(TAG, "Failed to open ringtone " + ringtoneUri);
         }
 
-        // Ringtone doesn't exist, use the fallback ringtone.
-        try {
-            AssetFileDescriptor afd = context.getResources().openRawResourceFd(
-                    com.android.internal.R.raw.fallbackring);
-            if (afd != null) {
-                Ringtone r = new Ringtone(context);
-                r.open(afd);
-                afd.close();
-                return r;
-            }
-        } catch (Exception ex) {
-        }
-        
-        // we should never get here
-        Log.e(TAG, "unable to find a usable ringtone");
         return null;
     }
     
@@ -638,8 +623,8 @@
     public static Uri getActualDefaultRingtoneUri(Context context, int type) {
         String setting = getSettingForType(type);
         if (setting == null) return null;
-        final String uriString = Settings.System.getString(context.getContentResolver(), setting); 
-        return uriString != null ? Uri.parse(uriString) : getValidRingtoneUri(context);
+        final String uriString = Settings.System.getString(context.getContentResolver(), setting);
+        return uriString != null ? Uri.parse(uriString) : null;
     }
     
     /**
@@ -655,7 +640,8 @@
     public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) {
         String setting = getSettingForType(type);
         if (setting == null) return;
-        Settings.System.putString(context.getContentResolver(), setting, ringtoneUri.toString());
+        Settings.System.putString(context.getContentResolver(), setting,
+                ringtoneUri != null ? ringtoneUri.toString() : null);
     }
     
     private static String getSettingForType(int type) {
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 1d960c5..95d61cd 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -59,6 +59,8 @@
 #include <media/PVPlayer.h>
 #include "TestPlayerStub.h"
 
+//#undef USE_STAGEFRIGHT
+
 #if USE_STAGEFRIGHT
 #include "StagefrightPlayer.h"
 #endif
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 5944d9c..5be9224 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -26,6 +26,7 @@
         SurfaceRenderer.cpp       \
         TimeSource.cpp            \
         TimedEventQueue.cpp       \
+        TIHardwareRenderer.cpp    \
         Utils.cpp                 \
         AudioPlayer.cpp           \
         ESDS.cpp                  \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 17c72b9..d547556 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -87,7 +87,10 @@
     } else {
         mAudioTrack = new AudioTrack(
                 AudioSystem::MUSIC, mSampleRate, AudioSystem::PCM_16_BIT,
-                numChannels, 8192, 0, &AudioCallback, this, 0);
+                (numChannels == 2)
+                    ? AudioSystem::CHANNEL_OUT_STEREO
+                    : AudioSystem::CHANNEL_OUT_MONO,
+                8192, 0, &AudioCallback, this, 0);
 
         assert(mAudioTrack->initCheck() == OK);
 
@@ -217,8 +220,10 @@
 
             Mutex::Autolock autoLock(mLock);
             mPositionTimeMediaUs = (int64_t)units * 1000000 / scale;
+
             mPositionTimeRealUs =
-                ((mNumFramesPlayed + size_done / 4) * 1000000) / mSampleRate; // XXX
+                ((mNumFramesPlayed + size_done / mFrameSize) * 1000000)
+                    / mSampleRate;
         }
 
         if (mInputBuffer->range_length() == 0) {
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 6b47a38..01cb2d9 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -73,8 +73,6 @@
 
     if (bitrate_index == 0 || bitrate_index == 0x0f) {
         // Disallow "free" bitrate.
-
-        LOGE("We disallow 'free' bitrate for now.");
         return false;
     }
 
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index caaec06..4c883c6 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -73,6 +73,8 @@
 
     bool mNeedsNALFraming;
 
+    uint8_t *mSrcBuffer;
+
     MPEG4Source(const MPEG4Source &);
     MPEG4Source &operator=(const MPEG4Source &);
 };
@@ -743,7 +745,8 @@
       mBuffer(NULL),
       mBufferOffset(0),
       mBufferSizeRemaining(0),
-      mNeedsNALFraming(false) {
+      mNeedsNALFraming(false),
+      mSrcBuffer(NULL) {
     const char *mime;
     bool success = mFormat->findCString(kKeyMIMEType, &mime);
     assert(success);
@@ -777,8 +780,13 @@
     status_t err = mSampleTable->getMaxSampleSize(&max_size);
     assert(err == OK);
 
-    // Add padding for de-framing of AVC content just in case.
-    mGroup->add_buffer(new MediaBuffer(max_size + 2));
+    // Assume that a given buffer only contains at most 10 fragments,
+    // each fragment originally prefixed with a 2 byte length will
+    // have a 4 byte header (0x00 0x00 0x00 0x01) after conversion,
+    // and thus will grow by 2 bytes per fragment.
+    mGroup->add_buffer(new MediaBuffer(max_size + 10 * 2));
+
+    mSrcBuffer = new uint8_t[max_size];
 
     mStarted = true;
 
@@ -793,6 +801,9 @@
         mBuffer = NULL;
     }
 
+    delete[] mSrcBuffer;
+    mSrcBuffer = NULL;
+
     delete mGroup;
     mGroup = NULL;
 
@@ -832,33 +843,31 @@
         // fall through
     }
 
-    if (mBuffer == NULL) {
-        off_t offset;
-        size_t size;
-        status_t err = mSampleTable->getSampleOffsetAndSize(
-                mCurrentSampleIndex, &offset, &size);
+    off_t offset;
+    size_t size;
+    status_t err = mSampleTable->getSampleOffsetAndSize(
+            mCurrentSampleIndex, &offset, &size);
 
-        if (err != OK) {
-            return err;
-        }
+    if (err != OK) {
+        return err;
+    }
 
-        uint32_t dts;
-        err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts);
+    uint32_t dts;
+    err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts);
 
-        if (err != OK) {
-            return err;
-        }
+    if (err != OK) {
+        return err;
+    }
 
-        err = mGroup->acquire_buffer(&mBuffer);
-        if (err != OK) {
-            assert(mBuffer == NULL);
-            return err;
-        }
+    err = mGroup->acquire_buffer(&mBuffer);
+    if (err != OK) {
+        assert(mBuffer == NULL);
+        return err;
+    }
 
-        assert(mBuffer->size() + 2 >= size);
-
+    if (!mIsAVC || !mNeedsNALFraming) {
         ssize_t num_bytes_read =
-            mDataSource->read_at(offset, (uint8_t *)mBuffer->data() + 2, size);
+            mDataSource->read_at(offset, (uint8_t *)mBuffer->data(), size);
 
         if (num_bytes_read < (ssize_t)size) {
             mBuffer->release();
@@ -867,50 +876,62 @@
             return err;
         }
 
-        mBuffer->set_range(2, size);
+        mBuffer->set_range(0, size);
         mBuffer->meta_data()->clear();
         mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
         mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
-
         ++mCurrentSampleIndex;
 
-        mBufferOffset = 2;
-        mBufferSizeRemaining = size;
-    }
-
-    if (!mIsAVC) {
         *out = mBuffer;
         mBuffer = NULL;
 
         return OK;
     }
 
-    uint8_t *data = (uint8_t *)mBuffer->data() + mBufferOffset;
-    assert(mBufferSizeRemaining >= 2);
+    ssize_t num_bytes_read =
+        mDataSource->read_at(offset, mSrcBuffer, size);
 
-    size_t nal_length = (data[0] << 8) | data[1];
-    assert(mBufferSizeRemaining >= 2 + nal_length);
-
-    if (mNeedsNALFraming) {
-        // Insert marker.
-        data[-2] = data[-1] = data[0] = 0;
-        data[1] = 1;
-
-        mBuffer->set_range(mBufferOffset - 2, nal_length + 4);
-    } else {
-        mBuffer->set_range(mBufferOffset + 2, nal_length);
-    }
-
-    mBufferOffset += nal_length + 2;
-    mBufferSizeRemaining -= nal_length + 2;
-
-    if (mBufferSizeRemaining > 0) {
-        *out = mBuffer->clone();
-    } else {
-        *out = mBuffer;
+    if (num_bytes_read < (ssize_t)size) {
+        mBuffer->release();
         mBuffer = NULL;
+
+        return err;
     }
 
+    uint8_t *dstData = (uint8_t *)mBuffer->data();
+    size_t srcOffset = 0;
+    size_t dstOffset = 0;
+    while (srcOffset < size) {
+        assert(srcOffset + 1 < size);
+        size_t nalLength =
+            (mSrcBuffer[srcOffset] << 8) | mSrcBuffer[srcOffset + 1];
+        assert(srcOffset + 1 + nalLength < size);
+        srcOffset += 2;
+
+        if (nalLength == 0) {
+            continue;
+        }
+
+        assert(dstOffset + 4 <= mBuffer->size());
+
+        dstData[dstOffset++] = 0;
+        dstData[dstOffset++] = 0;
+        dstData[dstOffset++] = 0;
+        dstData[dstOffset++] = 1;
+        memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
+        srcOffset += nalLength;
+        dstOffset += nalLength;
+    }
+
+    mBuffer->set_range(0, dstOffset);
+    mBuffer->meta_data()->clear();
+    mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
+    mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
+    ++mCurrentSampleIndex;
+
+    *out = mBuffer;
+    mBuffer = NULL;
+
     return OK;
 }
 
diff --git a/media/libstagefright/MediaPlayerImpl.cpp b/media/libstagefright/MediaPlayerImpl.cpp
index 78fcdee..04c9a11 100644
--- a/media/libstagefright/MediaPlayerImpl.cpp
+++ b/media/libstagefright/MediaPlayerImpl.cpp
@@ -40,6 +40,7 @@
 #include <media/stagefright/SoftwareRenderer.h>
 #include <media/stagefright/SurfaceRenderer.h>
 #include <media/stagefright/TimeSource.h>
+#include <media/stagefright/TIHardwareRenderer.h>
 #include <ui/PixelFormat.h>
 #include <ui/Surface.h>
 
@@ -311,6 +312,9 @@
         {
             Mutex::Autolock autoLock(mLock);
             mVideoPosition = pts_us;
+
+            LOGV("now_video = %.2f secs (%lld ms)",
+                 pts_us / 1E6, (pts_us + 500) / 1000);
         }
 
         if (seeking && mAudioPlayer != NULL) {
@@ -344,6 +348,7 @@
         if (mAudioPlayer != NULL
             && mAudioPlayer->getMediaTimeMapping(&realtime_us, &mediatime_us)) {
             mTimeSourceDeltaUs = realtime_us - mediatime_us;
+            LOGV("mTimeSourceDeltaUs = %.2f secs", mTimeSourceDeltaUs / 1E6);
         }
 
         int64_t now_us = mTimeSource->getRealTimeUs();
@@ -436,6 +441,7 @@
 }
 
 void MediaPlayerImpl::setAudioSource(MediaSource *source) {
+    LOGI("setAudioSource");
     mAudioSource = source;
 
     sp<MetaData> meta = source->getFormat();
@@ -646,17 +652,28 @@
     success = success && meta->findInt32(kKeyHeight, &decodedHeight);
     assert(success);
 
+    static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
     if (mSurface.get() != NULL) {
+        LOGW("Using SurfaceRenderer.");
         mRenderer =
             new SurfaceRenderer(
                     mSurface, mVideoWidth, mVideoHeight,
                     decodedWidth, decodedHeight);
-    } else if (format == OMX_COLOR_FormatYUV420Planar
-        && !strncasecmp(component, "OMX.qcom.video.decoder.", 23)) {
+    } else if (format == OMX_QCOM_COLOR_FormatYVU420SemiPlanar
+        && !strncmp(component, "OMX.qcom.video.decoder.", 23)) {
+        LOGW("Using QComHardwareRenderer.");
         mRenderer =
             new QComHardwareRenderer(
                     mISurface, mVideoWidth, mVideoHeight,
                     decodedWidth, decodedHeight);
+    } else if (format == OMX_COLOR_FormatCbYCrY
+            && !strcmp(component, "OMX.TI.Video.Decoder")) {
+        LOGW("Using TIHardwareRenderer.");
+        mRenderer =
+            new TIHardwareRenderer(
+                    mISurface, mVideoWidth, mVideoHeight,
+                    decodedWidth, decodedHeight);
     } else {
         LOGW("Using software renderer.");
         mRenderer = new SoftwareRenderer(
diff --git a/media/libstagefright/OMXDecoder.cpp b/media/libstagefright/OMXDecoder.cpp
index c059a9d..5e44999 100644
--- a/media/libstagefright/OMXDecoder.cpp
+++ b/media/libstagefright/OMXDecoder.cpp
@@ -20,6 +20,7 @@
 
 #undef NDEBUG
 #include <assert.h>
+#include <ctype.h>
 
 #include <OMX_Component.h>
 
@@ -54,14 +55,20 @@
 };
 
 static const CodecInfo kDecoderInfo[] = {
+    { "audio/mpeg", "OMX.TI.MP3.decode" },
     { "audio/mpeg", "OMX.PV.mp3dec" },
+    { "audio/3gpp", "OMX.TI.AMR.decode" },
     { "audio/3gpp", "OMX.PV.amrdec" },
+    { "audio/mp4a-latm", "OMX.TI.AAC.decode" },
     { "audio/mp4a-latm", "OMX.PV.aacdec" },
     { "video/mp4v-es", "OMX.qcom.video.decoder.mpeg4" },
+    { "video/mp4v-es", "OMX.TI.Video.Decoder" },
     { "video/mp4v-es", "OMX.PV.mpeg4dec" },
     { "video/3gpp", "OMX.qcom.video.decoder.h263" },
+    { "video/3gpp", "OMX.TI.Video.Decoder" },
     { "video/3gpp", "OMX.PV.h263dec" },
     { "video/avc", "OMX.qcom.video.decoder.avc" },
+    { "video/avc", "OMX.TI.Video.Decoder" },
     { "video/avc", "OMX.PV.avcdec" },
 };
 
@@ -92,7 +99,9 @@
 }
 
 // static
-OMXDecoder *OMXDecoder::Create(OMXClient *client, const sp<MetaData> &meta) {
+OMXDecoder *OMXDecoder::Create(
+        OMXClient *client, const sp<MetaData> &meta,
+        bool createEncoder) {
     const char *mime;
     bool success = meta->findCString(kKeyMIMEType, &mime);
     assert(success);
@@ -102,9 +111,15 @@
     const char *codec = NULL;
     IOMX::node_id node = 0;
     for (int index = 0;; ++index) {
-        codec = GetCodec(
-                kDecoderInfo, sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),
-                mime, index);
+        if (createEncoder) {
+            codec = GetCodec(
+                    kEncoderInfo, sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]),
+                    mime, index);
+        } else {
+            codec = GetCodec(
+                    kDecoderInfo, sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),
+                    mime, index);
+        }
 
         if (!codec) {
             return NULL;
@@ -118,7 +133,33 @@
         }
     }
 
-    OMXDecoder *decoder = new OMXDecoder(client, node, mime, codec);
+    uint32_t quirks = 0;
+    if (!strcmp(codec, "OMX.PV.avcdec")) {
+        quirks |= kWantsRawNALFrames;
+    }
+    if (!strcmp(codec, "OMX.TI.AAC.decode")
+        || !strcmp(codec, "OMX.TI.MP3.decode")) {
+        quirks |= kDoesntReturnBuffersOnDisable;
+    }
+    if (!strcmp(codec, "OMX.TI.AAC.decode")) {
+        quirks |= kDoesntFlushOnExecutingToIdle;
+        quirks |= kDoesntProperlyFlushAllPortsAtOnce;
+    }
+    if (!strncmp(codec, "OMX.qcom.video.encoder.", 23)) {
+        quirks |= kRequiresAllocateBufferOnInputPorts;
+    }
+    if (!strncmp(codec, "OMX.qcom.video.decoder.", 23)) {
+        quirks |= kRequiresAllocateBufferOnOutputPorts;
+    }
+    if (!strncmp(codec, "OMX.qcom.video.", 15)) {
+        quirks |= kRequiresLoadedToIdleAfterAllocation;
+    }
+    if (!strcmp(codec, "OMX.TI.AAC.decode")
+        || !strcmp(codec, "OMX.TI.MP3.decode")) {
+        quirks |= kMeasuresTimeInMilliseconds;
+    }
+
+    OMXDecoder *decoder = new OMXDecoder(client, node, mime, codec, quirks);
 
     uint32_t type;
     const void *data;
@@ -169,52 +210,22 @@
     return decoder;
 }
 
-// static
-OMXDecoder *OMXDecoder::CreateEncoder(
-        OMXClient *client, const sp<MetaData> &meta) {
-    const char *mime;
-    bool success = meta->findCString(kKeyMIMEType, &mime);
-    assert(success);
-
-    sp<IOMX> omx = client->interface();
-
-    const char *codec = NULL;
-    IOMX::node_id node = 0;
-    for (int index = 0;; ++index) {
-        codec = GetCodec(
-                kEncoderInfo, sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]),
-                mime, index);
-
-        if (!codec) {
-            return NULL;
-        }
-
-        LOGI("Attempting to allocate OMX node '%s'", codec);
-
-        status_t err = omx->allocate_node(codec, &node);
-        if (err == OK) {
-            break;
-        }
-    }
-
-    OMXDecoder *encoder = new OMXDecoder(client, node, mime, codec);
-
-    return encoder;
-}
-
 OMXDecoder::OMXDecoder(OMXClient *client, IOMX::node_id node,
-                       const char *mime, const char *codec)
+                       const char *mime, const char *codec,
+                       uint32_t quirks)
     : mClient(client),
       mOMX(mClient->interface()),
       mNode(node),
       mComponentName(strdup(codec)),
       mIsMP3(!strcasecmp(mime, "audio/mpeg")),
+      mIsAVC(!strcasecmp(mime, "video/avc")),
+      mQuirks(quirks),
       mSource(NULL),
       mCodecSpecificDataIterator(mCodecSpecificData.begin()),
       mState(OMX_StateLoaded),
       mPortStatusMask(kPortStatusActive << 2 | kPortStatusActive),
       mShutdownInitiated(false),
-      mDealer(new MemoryDealer(3 * 1024 * 1024)),
+      mDealer(new MemoryDealer(5 * 1024 * 1024)),
       mSeeking(false),
       mStarted(false),
       mErrorCondition(OK),
@@ -261,7 +272,7 @@
     // mDealer->dump("Decoder Dealer");
 
     sp<MetaData> params = new MetaData;
-    if (!strcmp(mComponentName, "OMX.qcom.video.decoder.avc")) {
+    if (mIsAVC && !(mQuirks & kWantsRawNALFrames)) {
         params->setInt32(kKeyNeedsNALFraming, true);
     }
 
@@ -297,7 +308,7 @@
     }
 
     int attempt = 1;
-    while (mState != OMX_StateLoaded && attempt < 10) {
+    while (mState != OMX_StateLoaded && attempt < 20) {
         usleep(100000);
 
         ++attempt;
@@ -366,7 +377,11 @@
             mOutputBuffers.erase(mOutputBuffers.begin());
         }
 
-        status_t err = mOMX->send_command(mNode, OMX_CommandFlush, -1);
+        // XXX One of TI's decoders appears to ignore a flush if it doesn't
+        // currently hold on to any buffers on the port in question and
+        // never sends the completion event... FIXME
+
+        status_t err = mOMX->send_command(mNode, OMX_CommandFlush, OMX_ALL);
         assert(err == OK);
 
         // Once flushing is completed buffers will again be scheduled to be
@@ -472,9 +487,121 @@
     assert(err == NO_ERROR);
 }
 
-void OMXDecoder::setVideoOutputFormat(OMX_U32 width, OMX_U32 height) {
+status_t OMXDecoder::setVideoPortFormatType(
+        OMX_U32 portIndex,
+        OMX_VIDEO_CODINGTYPE compressionFormat,
+        OMX_COLOR_FORMATTYPE colorFormat) {
+    OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+    format.nSize = sizeof(format);
+    format.nVersion.s.nVersionMajor = 1;
+    format.nVersion.s.nVersionMinor = 1;
+    format.nPortIndex = portIndex;
+    format.nIndex = 0;
+    bool found = false;
+
+    OMX_U32 index = 0;
+    for (;;) {
+        format.nIndex = index;
+        status_t err = mOMX->get_parameter(
+                mNode, OMX_IndexParamVideoPortFormat,
+                &format, sizeof(format));
+
+        if (err != OK) {
+            return err;
+        }
+
+        // The following assertion is violated by TI's video decoder.
+        // assert(format.nIndex == index);
+
+        if (format.eCompressionFormat == compressionFormat
+            && format.eColorFormat == colorFormat) {
+            found = true;
+            break;
+        }
+
+        ++index;
+    }
+
+    if (!found) {
+        return UNKNOWN_ERROR;
+    }
+
+    status_t err = mOMX->set_parameter(
+            mNode, OMX_IndexParamVideoPortFormat,
+            &format, sizeof(format));
+
+    return err;
+}
+
+#if 1
+void OMXDecoder::setVideoOutputFormat(
+        const char *mime, OMX_U32 width, OMX_U32 height) {
     LOGI("setVideoOutputFormat width=%ld, height=%ld", width, height);
 
+#if 1
+    // Enabling this code appears to be the right thing(tm), but,...
+    // the TI decoder then loses the ability to output YUV420 and only outputs
+    // YCbYCr (16bit)
+    if (!strcasecmp("video/avc", mime)) {
+        OMX_PARAM_COMPONENTROLETYPE role;
+        role.nSize = sizeof(role);
+        role.nVersion.s.nVersionMajor = 1;
+        role.nVersion.s.nVersionMinor = 1;
+        strncpy((char *)role.cRole, "video_decoder.avc",
+                OMX_MAX_STRINGNAME_SIZE - 1);
+        role.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
+
+        status_t err = mOMX->set_parameter(
+                mNode, OMX_IndexParamStandardComponentRole,
+                &role, sizeof(role));
+        assert(err == OK);
+    }
+#endif
+
+    OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
+    if (!strcasecmp("video/avc", mime)) {
+        compressionFormat = OMX_VIDEO_CodingAVC;
+    } else if (!strcasecmp("video/mp4v-es", mime)) {
+        compressionFormat = OMX_VIDEO_CodingMPEG4;
+    } else if (!strcasecmp("video/3gpp", mime)) {
+        compressionFormat = OMX_VIDEO_CodingH263;
+    } else {
+        assert(!"Should not be here. Not a supported video mime type.");
+    }
+
+    setVideoPortFormatType(
+            kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);
+
+#if 1
+    {
+        OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+        format.nSize = sizeof(format);
+        format.nVersion.s.nVersionMajor = 1;
+        format.nVersion.s.nVersionMinor = 1;
+        format.nPortIndex = kPortIndexOutput;
+        format.nIndex = 0;
+
+        status_t err = mOMX->get_parameter(
+                mNode, OMX_IndexParamVideoPortFormat,
+                &format, sizeof(format));
+        assert(err == OK);
+
+        assert(format.eCompressionFormat == OMX_VIDEO_CodingUnused);
+
+        static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
+        assert(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
+               || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
+               || format.eColorFormat == OMX_COLOR_FormatCbYCrY
+               || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar);
+
+        err = mOMX->set_parameter(
+                mNode, OMX_IndexParamVideoPortFormat,
+                &format, sizeof(format));
+        assert(err == OK);
+    }
+#endif
+
     OMX_PARAM_PORTDEFINITIONTYPE def;
     OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
 
@@ -502,7 +629,7 @@
     
     video_def->nFrameWidth = width;
     video_def->nFrameHeight = height;
-    // video_def.eCompressionFormat = OMX_VIDEO_CodingAVC;
+
     video_def->eColorFormat = OMX_COLOR_FormatUnused;
 
     err = mOMX->set_parameter(
@@ -522,21 +649,189 @@
 
     assert(def.eDomain == OMX_PortDomainVideo);
     
+#if 0
     def.nBufferSize =
         (((width + 15) & -16) * ((height + 15) & -16) * 3) / 2;  // YUV420
+#endif
 
     video_def->nFrameWidth = width;
     video_def->nFrameHeight = height;
-    video_def->nStride = width;
-    // video_def->nSliceHeight = height;
-    video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
-//    video_def->eColorFormat = OMX_COLOR_FormatYUV420Planar;
 
     err = mOMX->set_parameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
     assert(err == NO_ERROR);
 }
 
+#else
+static void hexdump(const void *_data, size_t size) {
+    char line[256];
+    char tmp[16];
+
+    const uint8_t *data = (const uint8_t *)_data;
+    size_t offset = 0;
+    while (offset < size) {
+        sprintf(line, "0x%04x  ", offset);
+
+        size_t n = size - offset;
+        if (n > 16) {
+            n = 16;
+        }
+
+        for (size_t i = 0; i < 16; ++i) {
+            if (i == 8) {
+                strcat(line, " ");
+            }
+
+            if (offset + i < size) {
+                sprintf(tmp, "%02x ", data[offset + i]);
+                strcat(line, tmp);
+            } else {
+                strcat(line, "   ");
+            }
+        }
+
+        strcat(line, " ");
+
+        for (size_t i = 0; i < n; ++i) {
+            if (isprint(data[offset + i])) {
+                sprintf(tmp, "%c", data[offset + i]);
+                strcat(line, tmp);
+            } else {
+                strcat(line, ".");
+            }
+        }
+
+        LOGI(line);
+
+        offset += 16;
+    }
+}
+
+static void DumpPortDefinitionType(const void *_param) {
+    OMX_PARAM_PORTDEFINITIONTYPE *param = (OMX_PARAM_PORTDEFINITIONTYPE *)_param;
+
+    LOGI("nPortIndex=%ld eDir=%s nBufferCountActual=%ld nBufferCountMin=%ld nBufferSize=%ld", param->nPortIndex, param->eDir == OMX_DirInput ? "input" : "output",
+        param->nBufferCountActual, param->nBufferCountMin, param->nBufferSize);
+
+    if (param->eDomain == OMX_PortDomainVideo) {
+        OMX_VIDEO_PORTDEFINITIONTYPE *video = &param->format.video;
+        LOGI("nFrameWidth=%ld nFrameHeight=%ld nStride=%ld nSliceHeight=%ld nBitrate=%ld xFramerate=%ld eCompressionFormat=%d eColorFormat=%d",
+            video->nFrameWidth, video->nFrameHeight, video->nStride, video->nSliceHeight, video->nBitrate, video->xFramerate, video->eCompressionFormat, video->eColorFormat);
+    } else {
+        hexdump(param, param->nSize);
+    }
+}
+
+void OMXDecoder::setVideoOutputFormat(
+        const char *mime, OMX_U32 width, OMX_U32 height) {
+    LOGI("setVideoOutputFormat width=%ld, height=%ld", width, height);
+
+#if 0
+    // Enabling this code appears to be the right thing(tm), but,...
+    // the decoder then loses the ability to output YUV420 and only outputs
+    // YCbYCr (16bit)
+    {
+        OMX_PARAM_COMPONENTROLETYPE role;
+        role.nSize = sizeof(role);
+        role.nVersion.s.nVersionMajor = 1;
+        role.nVersion.s.nVersionMinor = 1;
+        strncpy((char *)role.cRole, "video_decoder.avc",
+                OMX_MAX_STRINGNAME_SIZE - 1);
+        role.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
+
+        status_t err = mOMX->set_parameter(
+                mNode, OMX_IndexParamStandardComponentRole,
+                &role, sizeof(role));
+        assert(err == OK);
+    }
+#endif
+
+    setVideoPortFormatType(
+            kPortIndexInput, OMX_VIDEO_CodingAVC, OMX_COLOR_FormatUnused);
+
+#if 1
+    {
+        OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+        format.nSize = sizeof(format);
+        format.nVersion.s.nVersionMajor = 1;
+        format.nVersion.s.nVersionMinor = 1;
+        format.nPortIndex = kPortIndexOutput;
+        format.nIndex = 0;
+
+        status_t err = mOMX->get_parameter(
+                mNode, OMX_IndexParamVideoPortFormat,
+                &format, sizeof(format));
+        assert(err == OK);
+
+        LOGI("XXX MyOMX_GetParameter OMX_IndexParamVideoPortFormat");
+        hexdump(&format, format.nSize);
+
+        assert(format.eCompressionFormat == OMX_VIDEO_CodingUnused);
+        assert(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
+               || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
+               || format.eColorFormat == OMX_COLOR_FormatCbYCrY);
+
+        err = mOMX->set_parameter(
+                mNode, OMX_IndexParamVideoPortFormat,
+                &format, sizeof(format));
+        assert(err == OK);
+    }
+#endif
+
+    OMX_PORT_PARAM_TYPE ptype;
+    ptype.nSize = sizeof(ptype);
+    ptype.nVersion.s.nVersionMajor = 1;
+    ptype.nVersion.s.nVersionMinor = 1;
+
+    status_t err = mOMX->get_parameter(
+            mNode, OMX_IndexParamVideoInit, &ptype, sizeof(ptype));
+    assert(err == OK);
+
+    LOGI("XXX MyOMX_GetParameter OMX_IndexParamVideoInit");
+    hexdump(&ptype, ptype.nSize);
+
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    def.nSize = sizeof(def);
+    def.nVersion.s.nVersionMajor = 1;
+    def.nVersion.s.nVersionMinor = 1;
+    def.nPortIndex = kPortIndexInput;
+
+    err = mOMX->get_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    assert(err == OK);
+
+    LOGI("XXX MyOMX_GetParameter OMX_IndexParamPortDefinition");
+    DumpPortDefinitionType(&def);
+
+    OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+    video_def->nFrameWidth = width;
+    video_def->nFrameHeight = height;
+
+    err = mOMX->set_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    assert(err == OK);
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    def.nPortIndex = kPortIndexOutput;
+
+    err = mOMX->get_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    assert(err == OK);
+
+    LOGI("XXX MyOMX_GetParameter OMX_IndexParamPortDefinition");
+    DumpPortDefinitionType(&def);
+
+    video_def->nFrameWidth = width;
+    video_def->nFrameHeight = height;
+
+    err = mOMX->set_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    assert(err == OK);
+}
+
+#endif
+
 void OMXDecoder::setup() {
     const sp<MetaData> &meta = mSource->getFormat();
 
@@ -554,7 +849,7 @@
         success = success && meta->findInt32(kKeyHeight, &height);
         assert(success);
 
-        setVideoOutputFormat(width, height);
+        setVideoOutputFormat(mime, width, height);
     }
 
     // dumpPortDefinition(0);
@@ -644,10 +939,7 @@
 }
 
 void OMXDecoder::onStart() {
-    bool needs_qcom_hack =
-        !strncmp(mComponentName, "OMX.qcom.video.", 15);
-
-    if (!needs_qcom_hack) {
+    if (!(mQuirks & kRequiresLoadedToIdleAfterAllocation)) {
         status_t err =
             mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
         assert(err == NO_ERROR);
@@ -656,7 +948,7 @@
     allocateBuffers(kPortIndexInput);
     allocateBuffers(kPortIndexOutput);
 
-    if (needs_qcom_hack) {
+    if (mQuirks & kRequiresLoadedToIdleAfterAllocation) {
         // XXX this should happen before AllocateBuffers, but qcom's
         // h264 vdec disagrees.
         status_t err =
@@ -691,13 +983,17 @@
 
     for (OMX_U32 i = 0; i < num_buffers; ++i) {
         sp<IMemory> mem = mDealer->allocate(buffer_size);
+        if (mem.get() == NULL) {
+            LOGE("[%s] allocating IMemory of size %ld FAILED.",
+                 mComponentName, buffer_size);
+        }
         assert(mem.get() != NULL);
 
         IOMX::buffer_id buffer;
         status_t err;
 
         if (port_index == kPortIndexInput
-            && !strncmp(mComponentName, "OMX.qcom.video.encoder.", 23)) {
+                && (mQuirks & kRequiresAllocateBufferOnInputPorts)) {
             // qcom's H.263 encoder appears to want to allocate its own input
             // buffers.
             err = mOMX->allocate_buffer_with_backup(mNode, port_index, mem, &buffer);
@@ -706,7 +1002,7 @@
                      mComponentName, err);
             }
         } else if (port_index == kPortIndexOutput
-            && !strncmp(mComponentName, "OMX.qcom.video.decoder.", 23)) {
+                && (mQuirks & kRequiresAllocateBufferOnOutputPorts)) {
 #if 1
             err = mOMX->allocate_buffer_with_backup(mNode, port_index, mem, &buffer);
 #else
@@ -817,18 +1113,59 @@
         case OMX_CommandFlush: {
             OMX_U32 port_index = data;
             LOGV("Port %ld flush complete.", port_index);
-            assert(getPortStatus(port_index) == kPortStatusFlushing);
 
-            setPortStatus(port_index, kPortStatusActive);
-            BufferList *buffers = &mBuffers.editItemAt(port_index);
-            while (!buffers->empty()) {
-                IOMX::buffer_id buffer = *buffers->begin();
-                buffers->erase(buffers->begin());
+            PortStatus status = getPortStatus(port_index);
 
-                if (port_index == kPortIndexInput) {
-                    postEmptyBufferDone(buffer);
-                } else {
-                    postInitialFillBuffer(buffer);
+            assert(status == kPortStatusFlushing
+                    || status == kPortStatusFlushingToDisabled
+                    || status == kPortStatusFlushingToShutdown);
+
+            switch (status) {
+                case kPortStatusFlushing:
+                {
+                    // This happens when we're flushing before a seek.
+                    setPortStatus(port_index, kPortStatusActive);
+                    BufferList *buffers = &mBuffers.editItemAt(port_index);
+                    while (!buffers->empty()) {
+                        IOMX::buffer_id buffer = *buffers->begin();
+                        buffers->erase(buffers->begin());
+
+                        if (port_index == kPortIndexInput) {
+                            postEmptyBufferDone(buffer);
+                        } else {
+                            postInitialFillBuffer(buffer);
+                        }
+                    }
+                    break;
+                }
+
+                case kPortStatusFlushingToDisabled:
+                {
+                    // Port settings have changed and the (buggy) OMX component
+                    // does not properly return buffers on disabling, we need to
+                    // do a flush first and _then_ disable the port in question.
+
+                    setPortStatus(port_index, kPortStatusDisabled);
+                    status_t err = mOMX->send_command(
+                            mNode, OMX_CommandPortDisable, port_index);
+                    assert(err == OK);
+
+                    freePortBuffers(port_index);
+                    break;
+                }
+
+                default:
+                {
+                    assert(status == kPortStatusFlushingToShutdown);
+
+                    setPortStatus(port_index, kPortStatusShutdown);
+                    if (getPortStatus(kPortIndexInput) == kPortStatusShutdown
+                        && getPortStatus(kPortIndexOutput) == kPortStatusShutdown) {
+                        status_t err = mOMX->send_command(
+                                mNode, OMX_CommandStateSet, OMX_StateIdle);
+                        assert(err == OK);
+                    }
+                    break;
                 }
             }
             break;
@@ -841,10 +1178,22 @@
 
 void OMXDecoder::onEventPortSettingsChanged(OMX_U32 port_index) {
     assert(getPortStatus(port_index) == kPortStatusActive);
-    setPortStatus(port_index, kPortStatusDisabled);
 
-    status_t err =
-        mOMX->send_command(mNode, OMX_CommandPortDisable, port_index);
+    status_t err;
+
+    if (mQuirks & kDoesntReturnBuffersOnDisable) {
+        // Decoder does not properly return our buffers when disabled...
+        // Need to flush port instead and _then_ disable.
+
+        setPortStatus(port_index, kPortStatusFlushingToDisabled);
+
+        err = mOMX->send_command(mNode, OMX_CommandFlush, port_index);
+    } else {
+        setPortStatus(port_index, kPortStatusDisabled);
+
+        err = mOMX->send_command(mNode, OMX_CommandPortDisable, port_index);
+    }
+
     assert(err == NO_ERROR);
 }
 
@@ -894,19 +1243,8 @@
             mClient->send_command(mNode, OMX_CommandStateSet, OMX_StateLoaded);
         assert(err == NO_ERROR);
 
-        BufferList *ibuffers = &mBuffers.editItemAt(kPortIndexInput);
-        for (BufferList::iterator it = ibuffers->begin();
-             it != ibuffers->end(); ++it) {
-            freeInputBuffer(*it);
-        }
-        ibuffers->clear();
-
-        BufferList *obuffers = &mBuffers.editItemAt(kPortIndexOutput);
-        for (BufferList::iterator it = obuffers->begin();
-             it != obuffers->end(); ++it) {
-            freeOutputBuffer(*it);
-        }
-        obuffers->clear();
+        freePortBuffers(kPortIndexInput);
+        freePortBuffers(kPortIndexOutput);
     }
 }
 
@@ -925,26 +1263,41 @@
 
     mShutdownInitiated = true;
 
-    status_t err =
-        mClient->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
-    assert(err == NO_ERROR);
+    status_t err;
+    if (mQuirks & kDoesntFlushOnExecutingToIdle) {
+        if (mQuirks & kDoesntProperlyFlushAllPortsAtOnce) {
+            err = mOMX->send_command(mNode, OMX_CommandFlush, kPortIndexInput);
+            assert(err == OK);
 
-    setPortStatus(kPortIndexInput, kPortStatusShutdown);
-    setPortStatus(kPortIndexOutput, kPortStatusShutdown);
+            err = mOMX->send_command(mNode, OMX_CommandFlush, kPortIndexOutput);
+        } else {
+            err = mOMX->send_command(mNode, OMX_CommandFlush, OMX_ALL);
+        }
+
+        setPortStatus(kPortIndexInput, kPortStatusFlushingToShutdown);
+        setPortStatus(kPortIndexOutput, kPortStatusFlushingToShutdown);
+    } else {
+        err = mClient->send_command(
+                mNode, OMX_CommandStateSet, OMX_StateIdle);
+
+        setPortStatus(kPortIndexInput, kPortStatusShutdown);
+        setPortStatus(kPortIndexOutput, kPortStatusShutdown);
+    }
+    assert(err == OK);
 }
 
 void OMXDecoder::setPortStatus(OMX_U32 port_index, PortStatus status) {
-    int shift = 2 * port_index;
+    int shift = 3 * port_index;
 
-    mPortStatusMask &= ~(3 << shift);
+    mPortStatusMask &= ~(7 << shift);
     mPortStatusMask |= status << shift;
 }
 
 OMXDecoder::PortStatus OMXDecoder::getPortStatus(
         OMX_U32 port_index) const {
-    int shift = 2 * port_index;
+    int shift = 3 * port_index;
 
-    return static_cast<PortStatus>((mPortStatusMask >> shift) & 3);
+    return static_cast<PortStatus>((mPortStatusMask >> shift) & 7);
 }
 
 void OMXDecoder::onEmptyBufferDone(IOMX::buffer_id buffer) {
@@ -964,6 +1317,8 @@
             break;
 
         case kPortStatusFlushing:
+        case kPortStatusFlushingToDisabled:
+        case kPortStatusFlushingToShutdown:
             LOGV("We're currently flushing, enqueue INPUT buffer %p.", buffer);
             mBuffers.editItemAt(kPortIndexInput).push_back(buffer);
             err = NO_ERROR;
@@ -980,7 +1335,9 @@
 void OMXDecoder::onFillBufferDone(const omx_message &msg) {
     IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer;
 
-    LOGV("[%s] onFillBufferDone (%p)", mComponentName, buffer);
+    LOGV("[%s] on%sFillBufferDone (%p, size:%ld)", mComponentName,
+         msg.type == omx_message::INITIAL_FILL_BUFFER ? "Initial" : "",
+         buffer, msg.u.extended_buffer_data.range_length);
 
     status_t err;
     switch (getPortStatus(kPortIndexOutput)) {
@@ -995,6 +1352,8 @@
             break;
 
         case kPortStatusFlushing:
+        case kPortStatusFlushingToDisabled:
+        case kPortStatusFlushingToShutdown:
             LOGV("We're currently flushing, enqueue OUTPUT buffer %p.", buffer);
             mBuffers.editItemAt(kPortIndexOutput).push_back(buffer);
             err = NO_ERROR;
@@ -1035,7 +1394,7 @@
 
         size_t range_length = 0;
 
-        if (!strcmp(mComponentName, "OMX.qcom.video.decoder.avc")) {
+        if (mIsAVC && !(mQuirks & kWantsRawNALFrames)) {
             assert((*mCodecSpecificDataIterator).size + 4 <= mem->size());
 
             memcpy(mem->pointer(), kNALStartCode, 4);
@@ -1142,15 +1501,18 @@
     OMX_TICKS timestamp = 0;
 
     if (success) {
-        // XXX units should be microseconds but PV treats them as milliseconds.
-        timestamp = ((OMX_S64)units * 1000) / scale;
+        if (mQuirks & kMeasuresTimeInMilliseconds) {
+            timestamp = ((OMX_S64)units * 1000) / scale;
+        } else {
+            timestamp = ((OMX_S64)units * 1000000) / scale;
+        }
     }
 
     input_buffer->release();
     input_buffer = NULL;
 
-    LOGV("[%s] Calling EmptyBuffer on buffer %p",
-         mComponentName, buffer);
+    LOGV("[%s] Calling EmptyBuffer on buffer %p size:%d flags:0x%08lx",
+         mComponentName, buffer, src_length, flags);
 
     status_t err2 = mClient->emptyBuffer(
             mNode, buffer, 0, src_length, flags, timestamp);
@@ -1169,8 +1531,16 @@
 
     media_buffer->meta_data()->clear();
 
-    media_buffer->meta_data()->setInt32(
-            kKeyTimeUnits, msg.u.extended_buffer_data.timestamp);
+    if (mQuirks & kMeasuresTimeInMilliseconds) {
+        media_buffer->meta_data()->setInt32(
+                kKeyTimeUnits,
+                msg.u.extended_buffer_data.timestamp);
+    } else {
+        media_buffer->meta_data()->setInt32(
+                kKeyTimeUnits,
+                (msg.u.extended_buffer_data.timestamp + 500) / 1000);
+    }
+
     media_buffer->meta_data()->setInt32(kKeyTimeScale, 1000);
 
     if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_SYNCFRAME) {
@@ -1198,7 +1568,9 @@
 
     PortStatus outputStatus = getPortStatus(kPortIndexOutput);
     if (outputStatus == kPortStatusShutdown
-            || outputStatus == kPortStatusFlushing) {
+            || outputStatus == kPortStatusFlushing
+            || outputStatus == kPortStatusFlushingToDisabled
+            || outputStatus == kPortStatusFlushingToShutdown) {
         mBuffers.editItemAt(kPortIndexOutput).push_back(buffer);
     } else {
         LOGV("[%s] Calling FillBuffer on buffer %p.", mComponentName, buffer);
@@ -1326,4 +1698,18 @@
     postMessage(msg);
 }
 
+void OMXDecoder::freePortBuffers(OMX_U32 port_index) {
+    BufferList *buffers = &mBuffers.editItemAt(port_index);
+    while (!buffers->empty()) {
+        IOMX::buffer_id buffer = *buffers->begin();
+        buffers->erase(buffers->begin());
+
+        if (port_index == kPortIndexInput) {
+            freeInputBuffer(buffer);
+        } else {
+            freeOutputBuffer(buffer);
+        }
+    }
+}
+
 }  // namespace android
diff --git a/media/libstagefright/TIHardwareRenderer.cpp b/media/libstagefright/TIHardwareRenderer.cpp
new file mode 100644
index 0000000..ba42ef4
--- /dev/null
+++ b/media/libstagefright/TIHardwareRenderer.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TIHardwareRenderer"
+#include <utils/Log.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <media/stagefright/TIHardwareRenderer.h>
+#include <ui/ISurface.h>
+#include <ui/Overlay.h>
+
+namespace android {
+
+////////////////////////////////////////////////////////////////////////////////
+
+TIHardwareRenderer::TIHardwareRenderer(
+        const sp<ISurface> &surface,
+        size_t displayWidth, size_t displayHeight,
+        size_t decodedWidth, size_t decodedHeight)
+    : mISurface(surface),
+      mDisplayWidth(displayWidth),
+      mDisplayHeight(displayHeight),
+      mDecodedWidth(decodedWidth),
+      mDecodedHeight(decodedHeight),
+      mFrameSize((mDecodedWidth * mDecodedHeight * 3) / 2) {
+    assert(mISurface.get() != NULL);
+    assert(mDecodedWidth > 0);
+    assert(mDecodedHeight > 0);
+
+    sp<OverlayRef> ref = mISurface->createOverlay(
+            mDisplayWidth, mDisplayHeight, OVERLAY_FORMAT_CbYCrY_422_I);
+
+    if (ref.get() == NULL) {
+        LOGE("Unable to create the overlay!");
+        return;
+    }
+
+    mOverlay = new Overlay(ref);
+
+    for (size_t i = 0; i < mOverlay->getBufferCount(); ++i) {
+        mOverlayAddresses.push(mOverlay->getBufferAddress((void *)i));
+    }
+    mIndex = mOverlayAddresses.size() - 1;
+}
+
+TIHardwareRenderer::~TIHardwareRenderer() {
+    if (mOverlay.get() != NULL) {
+        mOverlay->destroy();
+        mOverlay.clear();
+
+        // XXX apparently destroying an overlay is an asynchronous process...
+        sleep(1);
+    }
+}
+
+void TIHardwareRenderer::render(
+        const void *data, size_t size, void *platformPrivate) {
+    // assert(size == mFrameSize);
+
+    if (mOverlay.get() == NULL) {
+        return;
+    }
+
+#if 0
+    overlay_buffer_t buffer;
+    if (mOverlay->dequeueBuffer(&buffer) == OK) {
+        void *addr = mOverlay->getBufferAddress(buffer);
+
+        memcpy(addr, data, size);
+
+        mOverlay->queueBuffer(buffer);
+    }
+#else
+    memcpy(mOverlayAddresses[mIndex], data, size);
+    mOverlay->queueBuffer((void *)mIndex);
+
+    if (mIndex-- == 0) {
+        mIndex = mOverlayAddresses.size() - 1;
+    }
+#endif
+}
+
+}  // namespace android
+
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 7f3f114..445e681 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -149,7 +149,7 @@
             hnd = new driver_t(dso);
         } else {
             // Always load EGL first
-            snprintf(path, PATH_MAX, "lib%s_%s.so", "EGL", tag);
+            snprintf(path, PATH_MAX, format, "EGL", tag);
             dso = load_driver(path, hooks, EGL);
             if (dso) {
                 hnd = new driver_t(dso);
diff --git a/packages/SettingsProvider/res/values-cs/defaults.xml b/packages/SettingsProvider/res/values-cs/defaults.xml
deleted file mode 100644
index a7c01b3..0000000
--- a/packages/SettingsProvider/res/values-cs/defaults.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="def_airplane_mode_radios">"mobil,bluetooth,wifi"</string>
-    <string name="def_location_providers_allowed">"gps"</string>
-    <!-- no translation found for def_backup_transport (6764822064303377157) -->
-    <skip />
-</resources>
diff --git a/packages/SettingsProvider/res/values-cs/strings.xml b/packages/SettingsProvider/res/values-cs/strings.xml
index dc75a92..2b089d9 100644
--- a/packages/SettingsProvider/res/values-cs/strings.xml
+++ b/packages/SettingsProvider/res/values-cs/strings.xml
@@ -15,5 +15,5 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label">"Paměť pro nastavení"</string>
+    <string name="app_label" msgid="4567566098528588863">"Paměť pro nastavení"</string>
 </resources>
diff --git a/packages/SettingsProvider/res/values-de/defaults.xml b/packages/SettingsProvider/res/values-de/defaults.xml
deleted file mode 100644
index f85d3f0..0000000
--- a/packages/SettingsProvider/res/values-de/defaults.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="def_airplane_mode_radios">"Mobilfunk, Bluetooth, WLAN"</string>
-    <string name="def_location_providers_allowed">"GPS"</string>
-    <!-- no translation found for def_backup_transport (6764822064303377157) -->
-    <skip />
-</resources>
diff --git a/packages/SettingsProvider/res/values-de/strings.xml b/packages/SettingsProvider/res/values-de/strings.xml
index 50c8a14..a293522 100644
--- a/packages/SettingsProvider/res/values-de/strings.xml
+++ b/packages/SettingsProvider/res/values-de/strings.xml
@@ -15,5 +15,5 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label">"Einstellungsspeicher"</string>
+    <string name="app_label" msgid="4567566098528588863">"Einstellungsspeicher"</string>
 </resources>
diff --git a/packages/SettingsProvider/res/values-es/defaults.xml b/packages/SettingsProvider/res/values-es/defaults.xml
deleted file mode 100644
index a64805a..0000000
--- a/packages/SettingsProvider/res/values-es/defaults.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="def_airplane_mode_radios">"móvil,bluetooth,wifi"</string>
-    <string name="def_location_providers_allowed">"gps"</string>
-    <!-- no translation found for def_backup_transport (6764822064303377157) -->
-    <skip />
-</resources>
diff --git a/packages/SettingsProvider/res/values-es/strings.xml b/packages/SettingsProvider/res/values-es/strings.xml
index d30d195..de3958b 100644
--- a/packages/SettingsProvider/res/values-es/strings.xml
+++ b/packages/SettingsProvider/res/values-es/strings.xml
@@ -15,5 +15,5 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label">"Almacenamiento de configuración"</string>
+    <string name="app_label" msgid="4567566098528588863">"Almacenamiento de configuración"</string>
 </resources>
diff --git a/packages/SettingsProvider/res/values-fr/defaults.xml b/packages/SettingsProvider/res/values-fr/defaults.xml
deleted file mode 100644
index 56334cc..0000000
--- a/packages/SettingsProvider/res/values-fr/defaults.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="def_airplane_mode_radios">"cellulaire, Bluetooth, Wi-Fi"</string>
-    <string name="def_location_providers_allowed">"gps"</string>
-    <!-- no translation found for def_backup_transport (6764822064303377157) -->
-    <skip />
-</resources>
diff --git a/packages/SettingsProvider/res/values-fr/strings.xml b/packages/SettingsProvider/res/values-fr/strings.xml
index 686ec8b..7a1386a 100644
--- a/packages/SettingsProvider/res/values-fr/strings.xml
+++ b/packages/SettingsProvider/res/values-fr/strings.xml
@@ -15,5 +15,5 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label">"Stockage des paramètres"</string>
+    <string name="app_label" msgid="4567566098528588863">"Stockage des paramètres"</string>
 </resources>
diff --git a/packages/SettingsProvider/res/values-it/defaults.xml b/packages/SettingsProvider/res/values-it/defaults.xml
deleted file mode 100644
index 19c0896..0000000
--- a/packages/SettingsProvider/res/values-it/defaults.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="def_airplane_mode_radios">"cellulare,bluetooth,wifi"</string>
-    <string name="def_location_providers_allowed">"gps"</string>
-    <!-- no translation found for def_backup_transport (6764822064303377157) -->
-    <skip />
-</resources>
diff --git a/packages/SettingsProvider/res/values-it/strings.xml b/packages/SettingsProvider/res/values-it/strings.xml
index 29e462f..f88a654 100644
--- a/packages/SettingsProvider/res/values-it/strings.xml
+++ b/packages/SettingsProvider/res/values-it/strings.xml
@@ -15,5 +15,5 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label">"Archiviazione impostazioni"</string>
+    <string name="app_label" msgid="4567566098528588863">"Archiviazione impostazioni"</string>
 </resources>
diff --git a/packages/SettingsProvider/res/values-nl/defaults.xml b/packages/SettingsProvider/res/values-nl/defaults.xml
deleted file mode 100644
index 625235a..0000000
--- a/packages/SettingsProvider/res/values-nl/defaults.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="def_airplane_mode_radios">"mobiel,bluetooth,wifi"</string>
-    <string name="def_location_providers_allowed">"gps"</string>
-    <!-- no translation found for def_backup_transport (6764822064303377157) -->
-    <skip />
-</resources>
diff --git a/packages/SettingsProvider/res/values-nl/strings.xml b/packages/SettingsProvider/res/values-nl/strings.xml
index b37b535..7a0e416 100644
--- a/packages/SettingsProvider/res/values-nl/strings.xml
+++ b/packages/SettingsProvider/res/values-nl/strings.xml
@@ -15,5 +15,5 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label">"Opslagruimte voor instellingen"</string>
+    <string name="app_label" msgid="4567566098528588863">"Opslagruimte voor instellingen"</string>
 </resources>
diff --git a/packages/SettingsProvider/res/values-pl/defaults.xml b/packages/SettingsProvider/res/values-pl/defaults.xml
deleted file mode 100644
index b60832e..0000000
--- a/packages/SettingsProvider/res/values-pl/defaults.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="def_airplane_mode_radios">"komórka,bluetooth,wifi"</string>
-    <string name="def_location_providers_allowed">"gps"</string>
-    <!-- no translation found for def_backup_transport (6764822064303377157) -->
-    <skip />
-</resources>
diff --git a/packages/SettingsProvider/res/values-pl/strings.xml b/packages/SettingsProvider/res/values-pl/strings.xml
index 4ab1e91..ccff82e3 100644
--- a/packages/SettingsProvider/res/values-pl/strings.xml
+++ b/packages/SettingsProvider/res/values-pl/strings.xml
@@ -15,5 +15,5 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label">"Pamięć ustawień"</string>
+    <string name="app_label" msgid="4567566098528588863">"Pamięć ustawień"</string>
 </resources>
diff --git a/packages/SettingsProvider/res/values-zh-rTW/defaults.xml b/packages/SettingsProvider/res/values-zh-rTW/defaults.xml
deleted file mode 100644
index fdbba88..0000000
--- a/packages/SettingsProvider/res/values-zh-rTW/defaults.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="def_airplane_mode_radios">"手機,藍牙,wifi"</string>
-    <string name="def_location_providers_allowed">"gps"</string>
-    <!-- no translation found for def_backup_transport (6764822064303377157) -->
-    <skip />
-</resources>
diff --git a/packages/SettingsProvider/res/values-zh-rTW/strings.xml b/packages/SettingsProvider/res/values-zh-rTW/strings.xml
index b24144a..0700a76 100644
--- a/packages/SettingsProvider/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsProvider/res/values-zh-rTW/strings.xml
@@ -15,5 +15,5 @@
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label">"設定儲存空間"</string>
+    <string name="app_label" msgid="4567566098528588863">"設定儲存空間"</string>
 </resources>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index af02741..c33bdd7 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -21,7 +21,7 @@
     <integer name="def_screen_off_timeout">60000</integer>
     <bool name="def_airplane_mode_on">false</bool>
     <!-- Comma-separated list of bluetooth, wifi, and cell. -->
-    <string name="def_airplane_mode_radios">cell,bluetooth,wifi</string>
+    <string name="def_airplane_mode_radios" translatable="false">cell,bluetooth,wifi</string>
     <bool name="def_auto_time">true</bool>
     <bool name="def_accelerometer_rotation">true</bool>
     <!-- Default screen brightness, from 0 to 255.  102 is 40%. -->
@@ -35,7 +35,7 @@
          Network location is off by default because it requires
          user opt-in via Setup Wizard or Settings.  
     -->
-    <string name="def_location_providers_allowed">gps</string>
+    <string name="def_location_providers_allowed" translatable="false">gps</string>
     <bool name="assisted_gps_enabled">true</bool>
     <!--  0 == mobile, 1 == wifi. -->
     <integer name="def_network_preference">1</integer>
@@ -44,5 +44,5 @@
     <bool name="def_networks_available_notification_on">true</bool>
     
     <bool name="def_backup_enabled">false</bool>
-    <string name="def_backup_transport"></string>
+    <string name="def_backup_transport" translatable="false"></string>
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1451682..9877342 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -24,9 +24,11 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteQueryBuilder;
+import android.media.Ringtone;
 import android.media.RingtoneManager;
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
@@ -397,12 +399,8 @@
             
             // Get the current value for the default sound
             Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
-            if (soundUri == null) {
-                // Fallback on any valid ringtone Uri
-                soundUri = RingtoneManager.getValidRingtoneUri(context);
-            }
 
-            if (soundUri != null) { 
+            if (soundUri != null) {
                 // Only proxy the openFile call to drm or media providers
                 String authority = soundUri.getAuthority();
                 boolean isDrmAuthority = authority.equals(DrmStore.AUTHORITY);
@@ -426,4 +424,64 @@
 
         return super.openFile(uri, mode);
     }
+
+    @Override
+    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
+
+        /*
+         * When a client attempts to openFile the default ringtone or
+         * notification setting Uri, we will proxy the call to the current
+         * default ringtone's Uri (if it is in the DRM or media provider).
+         */
+        int ringtoneType = RingtoneManager.getDefaultType(uri);
+        // Above call returns -1 if the Uri doesn't match a default type
+        if (ringtoneType != -1) {
+            Context context = getContext();
+
+            // Get the current value for the default sound
+            Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
+
+            if (soundUri != null) {
+                // Only proxy the openFile call to drm or media providers
+                String authority = soundUri.getAuthority();
+                boolean isDrmAuthority = authority.equals(DrmStore.AUTHORITY);
+                if (isDrmAuthority || authority.equals(MediaStore.AUTHORITY)) {
+
+                    if (isDrmAuthority) {
+                        try {
+                            // Check DRM access permission here, since once we
+                            // do the below call the DRM will be checking our
+                            // permission, not our caller's permission
+                            DrmStore.enforceAccessDrmPermission(context);
+                        } catch (SecurityException e) {
+                            throw new FileNotFoundException(e.getMessage());
+                        }
+                    }
+
+                    ParcelFileDescriptor pfd = null;
+                    try {
+                        pfd = context.getContentResolver().openFileDescriptor(soundUri, mode);
+                        return new AssetFileDescriptor(pfd, 0, -1);
+                    } catch (FileNotFoundException ex) {
+                        // fall through and open the fallback ringtone below
+                    }
+                }
+
+                try {
+                    return super.openAssetFile(soundUri, mode);
+                } catch (FileNotFoundException ex) {
+                    // Since a non-null Uri was specified, but couldn't be opened,
+                    // fall back to the built-in ringtone.
+                    return context.getResources().openRawResourceFd(
+                            com.android.internal.R.raw.fallbackring);
+                }
+            }
+            // no need to fall through and have openFile() try again, since we
+            // already know that will fail.
+            throw new FileNotFoundException(); // or return null ?
+        }
+
+        // Note that this will end up calling openFile() above.
+        return super.openAssetFile(uri, mode);
+    }
 }
diff --git a/preloaded-classes b/preloaded-classes
index 3858883..6eb3cf2 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -845,16 +845,18 @@
 java.util.HashMap
 java.util.HashMap$1
 java.util.HashMap$2
-java.util.HashMap$2$1
+java.util.HashMap$AbstractMapIterator
 java.util.HashMap$Entry
+java.util.HashMap$EntryIterator
 java.util.HashMap$HashMapEntrySet
-java.util.HashMap$HashMapEntrySet$1
+java.util.HashMap$KeyIterator
+java.util.HashMap$ValueIterator
 java.util.HashSet
 java.util.Hashtable
-java.util.Hashtable$4
-java.util.Hashtable$4$1
+java.util.Hashtable$6
+java.util.Hashtable$6$1
 java.util.Hashtable$Entry
-java.util.Hashtable$HashEnumerator
+java.util.Hashtable$HashEnumIterator
 java.util.Hashtable$HashIterator
 java.util.IdentityHashMap
 java.util.LinkedHashMap
@@ -869,7 +871,7 @@
 java.util.SimpleTimeZone
 java.util.TimeZone
 java.util.TreeMap
-java.util.TreeMap$Entry
+java.util.TreeMap$MapEntry
 java.util.TreeSet
 java.util.Vector
 java.util.WeakHashMap
@@ -952,14 +954,11 @@
 org.apache.harmony.luni.internal.net.www.protocol.jar.Handler
 org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnection
 org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnection$1
-org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnection$CacheEntry
 org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnection$JarURLConnectionInputStream
-org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnection$LRUComparator
 org.apache.harmony.luni.internal.util.TimezoneGetter
 org.apache.harmony.luni.internal.util.ZoneInfo
 org.apache.harmony.luni.internal.util.ZoneInfoDB
 org.apache.harmony.luni.net.PlainSocketImpl
-org.apache.harmony.luni.net.PlainSocketImpl2
 org.apache.harmony.luni.platform.PlatformAddress
 org.apache.harmony.luni.util.TwoKeyHashMap
 org.apache.harmony.nio.internal.FileChannelImpl$RepositioningLock
@@ -1176,4 +1175,3 @@
 org.xmlpull.v1.XmlPullParserFactory
 org.xmlpull.v1.sax2.Driver
 sun.misc.Unsafe
-
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 4643115..67f3816 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -46,6 +46,8 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.EventLog;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -91,6 +93,20 @@
     private static final int MSG_RUN_RESTORE = 3;
     private static final int MSG_RUN_CLEAR = 4;
 
+    // Event tags -- see system/core/logcat/event-log-tags
+    private static final int BACKUP_DATA_CHANGED_EVENT = 2820;
+    private static final int BACKUP_START_EVENT = 2821;
+    private static final int BACKUP_TRANSPORT_FAILURE_EVENT = 2822;
+    private static final int BACKUP_AGENT_FAILURE_EVENT = 2823;
+    private static final int BACKUP_PACKAGE_EVENT = 2824;
+    private static final int BACKUP_SUCCESS_EVENT = 2825;
+
+    private static final int RESTORE_START_EVENT = 2830;
+    private static final int RESTORE_TRANSPORT_FAILURE_EVENT = 2831;
+    private static final int RESTORE_AGENT_FAILURE_EVENT = 2832;
+    private static final int RESTORE_PACKAGE_EVENT = 2833;
+    private static final int RESTORE_SUCCESS_EVENT = 2834;
+
     // Timeout interval for deciding that a bind or clear-data has taken too long
     static final long TIMEOUT_INTERVAL = 10 * 1000;
 
@@ -787,7 +803,7 @@
 
     class ClearDataObserver extends IPackageDataObserver.Stub {
         public void onRemoveCompleted(String packageName, boolean succeeded)
-                throws android.os.RemoteException {
+                throws RemoteException {
             synchronized(mClearDataLock) {
                 mClearingData = false;
                 mClearDataLock.notifyAll();
@@ -820,43 +836,49 @@
 
         @Override
         public void run() {
+            long startRealtime = SystemClock.elapsedRealtime();
             if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
 
             // Backups run at background priority
             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
 
-            // The package manager doesn't have a proper <application> etc, but since
-            // it's running here in the system process we can just set up its agent
-            // directly and use a synthetic BackupRequest.  We always run this pass
-            // because it's cheap and this way we guarantee that we don't get out of
-            // step even if we're selecting among various transports at run time.
-            PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
-                    mPackageManager, allAgentPackages());
-            BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
-            pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
-            processOneBackup(pmRequest,
-                    IBackupAgent.Stub.asInterface(pmAgent.onBind()),
-                    mTransport);
-
-            // Now run all the backups in our queue
-            doQueuedBackups(mTransport);
-
-            // Finally, tear down the transport
             try {
-                if (!mTransport.finishBackup()) {
-                    // STOPSHIP TODO: handle errors
-                    Log.e(TAG, "Backup failure in finishBackup()");
+                EventLog.writeEvent(BACKUP_START_EVENT, mTransport.transportDirName());
+
+                // The package manager doesn't have a proper <application> etc, but since
+                // it's running here in the system process we can just set up its agent
+                // directly and use a synthetic BackupRequest.  We always run this pass
+                // because it's cheap and this way we guarantee that we don't get out of
+                // step even if we're selecting among various transports at run time.
+                PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+                        mPackageManager, allAgentPackages());
+                BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
+                pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
+                processOneBackup(pmRequest,
+                        IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
+
+                // Now run all the backups in our queue
+                int count = mQueue.size();
+                doQueuedBackups(mTransport);
+
+                // Finally, tear down the transport
+                if (mTransport.finishBackup()) {
+                    int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
+                    EventLog.writeEvent(BACKUP_SUCCESS_EVENT, count, millis);
+                } else {
+                    EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, "");
+                    Log.e(TAG, "Transport error in finishBackup()");
                 }
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error in finishBackup()", e);
-            }
 
-            if (!mJournal.delete()) {
-                Log.e(TAG, "Unable to remove backup journal file " + mJournal.getAbsolutePath());
+                if (!mJournal.delete()) {
+                    Log.e(TAG, "Unable to remove backup journal file " + mJournal);
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Error in backup thread", e);
+            } finally {
+                // Only once we're entirely finished do we release the wakelock
+                mWakelock.release();
             }
-
-            // Only once we're entirely finished do we release the wakelock
-            mWakelock.release();
         }
 
         private void doQueuedBackups(IBackupTransport transport) {
@@ -882,19 +904,27 @@
                     Log.v(TAG, "bind/backup threw");
                     e.printStackTrace();
                 }
-
             }
         }
 
         void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
             final String packageName = request.appInfo.packageName;
-            Log.d(TAG, "processOneBackup doBackup() on " + packageName);
+            if (DEBUG) Log.d(TAG, "processOneBackup doBackup() on " + packageName);
 
+            // !!! 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");
+
+            ParcelFileDescriptor savedState = null;
+            ParcelFileDescriptor backupData = null;
+            ParcelFileDescriptor newState = null;
+
+            PackageInfo packInfo;
             try {
                 // Look up the package info & signatures.  This is first so that if it
                 // throws an exception, there's no file setup yet that would need to
                 // be unraveled.
-                PackageInfo packInfo;
                 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
                     // The metadata 'package' is synthetic
                     packInfo = new PackageInfo();
@@ -904,69 +934,65 @@
                         PackageManager.GET_SIGNATURES);
                 }
 
-                // !!! 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"
-                ParcelFileDescriptor savedState = (request.fullBackup) ? null
-                        : ParcelFileDescriptor.open(savedStateName,
+                if (!request.fullBackup) {
+                    savedState = ParcelFileDescriptor.open(savedStateName,
                             ParcelFileDescriptor.MODE_READ_ONLY |
-                            ParcelFileDescriptor.MODE_CREATE);
+                            ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
+                }
 
-                backupDataName.delete();
-                ParcelFileDescriptor backupData =
-                        ParcelFileDescriptor.open(backupDataName,
-                                ParcelFileDescriptor.MODE_READ_WRITE |
-                                ParcelFileDescriptor.MODE_CREATE);
+                backupData = ParcelFileDescriptor.open(backupDataName,
+                        ParcelFileDescriptor.MODE_READ_WRITE |
+                        ParcelFileDescriptor.MODE_CREATE |
+                        ParcelFileDescriptor.MODE_TRUNCATE);
 
-                newStateName.delete();
-                ParcelFileDescriptor newState =
-                        ParcelFileDescriptor.open(newStateName,
-                                ParcelFileDescriptor.MODE_READ_WRITE |
-                                ParcelFileDescriptor.MODE_CREATE);
+                newState = ParcelFileDescriptor.open(newStateName,
+                        ParcelFileDescriptor.MODE_READ_WRITE |
+                        ParcelFileDescriptor.MODE_CREATE |
+                        ParcelFileDescriptor.MODE_TRUNCATE);
 
                 // Run the target's backup pass
-                boolean success = false;
-                try {
-                    agent.doBackup(savedState, backupData, newState);
-                    logBackupComplete(packageName);
-                    success = true;
-                } finally {
-                    if (savedState != null) {
-                        savedState.close();
-                    }
-                    backupData.close();
-                    newState.close();
-                }
-
-                // Now propagate the newly-backed-up data to the transport
-                if (success) {
-                    if (DEBUG) Log.v(TAG, "doBackup() success");
-                    if (backupDataName.length() > 0) {
-                        backupData =
-                            ParcelFileDescriptor.open(backupDataName,
-                                    ParcelFileDescriptor.MODE_READ_ONLY);
-                        if (!transport.performBackup(packInfo, backupData)) {
-                            // STOPSHIP TODO: handle errors
-                            Log.e(TAG, "Backup failure in performBackup()");
-                        }
-                    } else {
-                        if (DEBUG) {
-                            Log.i(TAG, "no backup data written; not calling transport");
-                        }
-                    }
-
-                    // After successful transport, delete the now-stale data
-                    // and juggle the files so that next time we supply the agent
-                    // with the new state file it just created.
-                    backupDataName.delete();
-                    newStateName.renameTo(savedStateName);
-                }
+                agent.doBackup(savedState, backupData, newState);
+                logBackupComplete(packageName);
+                if (DEBUG) Log.v(TAG, "doBackup() success");
             } catch (Exception e) {
                 Log.e(TAG, "Error backing up " + packageName, e);
+                EventLog.writeEvent(BACKUP_AGENT_FAILURE_EVENT, packageName, e.toString());
+                backupDataName.delete();
+                newStateName.delete();
+                return;
+            } finally {
+                try { if (savedState != null) savedState.close(); } catch (IOException e) {}
+                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
+                try { if (newState != null) newState.close(); } catch (IOException e) {}
+                savedState = backupData = newState = null;
+            }
+
+            // Now propagate the newly-backed-up data to the transport
+            try {
+                int size = (int) backupDataName.length();
+                if (size > 0) {
+                    backupData = ParcelFileDescriptor.open(backupDataName,
+                            ParcelFileDescriptor.MODE_READ_ONLY);
+
+                    if (!transport.performBackup(packInfo, backupData)) throw new Exception();
+                } else {
+                    if (DEBUG) Log.i(TAG, "no backup data written; not calling transport");
+                }
+
+                // After successful transport, delete the now-stale data
+                // and juggle the files so that next time we supply the agent
+                // with the new state file it just created.
+                backupDataName.delete();
+                newStateName.renameTo(savedStateName);
+                EventLog.writeEvent(BACKUP_PACKAGE_EVENT, packageName, size);
+            } catch (Exception e) {
+                Log.e(TAG, "Transport error backing up " + packageName, e);
+                EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName);
+                return;
+            } finally {
+                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
             }
         }
     }
@@ -1012,7 +1038,6 @@
         private IBackupTransport mTransport;
         private IRestoreObserver mObserver;
         private long mToken;
-        private RestoreSet mImage;
         private File mStateDir;
 
         class RestoreRequest {
@@ -1042,6 +1067,7 @@
 
         @Override
         public void run() {
+            long startRealtime = SystemClock.elapsedRealtime();
             if (DEBUG) Log.v(TAG, "Beginning restore process mTransport=" + mTransport
                     + " mObserver=" + mObserver + " mToken=" + mToken);
             /**
@@ -1049,7 +1075,7 @@
              *
              * 1. get the restore set description for our identity
              * 2. for each app in the restore set:
-             *    3.a. if it's restorable on this device, add it to the restore queue
+             *    2.a. if it's restorable on this device, add it to the restore queue
              * 3. for each app in the restore queue:
              *    3.a. clear the app data
              *    3.b. get the restore data for the app from the transport
@@ -1057,25 +1083,18 @@
              *    3.d. agent.doRestore() with the data from the server
              *    3.e. unbind the agent [and kill the app?]
              * 4. shut down the transport
+             *
+             * On errors, we try our best to recover and move on to the next
+             * application, but if necessary we abort the whole operation --
+             * the user is waiting, after al.
              */
 
             int error = -1; // assume error
 
             // build the set of apps to restore
             try {
-                RestoreSet[] images = mTransport.getAvailableRestoreSets();
-                if (images == null) {
-                    // STOPSHIP TODO: Handle the failure somehow?
-                    Log.e(TAG, "Error getting restore sets");
-                    return;
-                }
-
-                if (images.length == 0) {
-                    Log.i(TAG, "No restore sets available");
-                    return;
-                }
-
-                mImage = images[0];
+                // TODO: Log this before getAvailableRestoreSets, somehow
+                EventLog.writeEvent(RESTORE_START_EVENT, mTransport.transportDirName());
 
                 // Get the list of all packages which have backup enabled.
                 // (Include the Package Manager metadata pseudo-package first.)
@@ -1100,22 +1119,26 @@
                 }
 
                 if (!mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0]))) {
-                    // STOPSHIP TODO: Handle the failure somehow?
                     Log.e(TAG, "Error starting restore operation");
+                    EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
                     return;
                 }
 
                 String packageName = mTransport.nextRestorePackage();
                 if (packageName == null) {
-                    // STOPSHIP TODO: Handle the failure somehow?
                     Log.e(TAG, "Error getting first restore package");
+                    EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
                     return;
                 } else if (packageName.equals("")) {
                     Log.i(TAG, "No restore data available");
+                    int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
+                    EventLog.writeEvent(RESTORE_SUCCESS_EVENT, 0, millis);
                     return;
                 } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
                     Log.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
                           + "\", found only \"" + packageName + "\"");
+                    EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, PACKAGE_MANAGER_SENTINEL,
+                            "Package manager data missing");
                     return;
                 }
 
@@ -1128,23 +1151,25 @@
                 // signature/version verification etc, so we simply do not proceed with
                 // the restore operation.
                 if (!pmAgent.hasMetadata()) {
-                    Log.i(TAG, "No restore metadata available, so not restoring settings");
+                    Log.e(TAG, "No restore metadata available, so not restoring settings");
+                    EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, PACKAGE_MANAGER_SENTINEL,
+                            "Package manager restore metadata missing");
                     return;
                 }
 
                 int count = 0;
                 for (;;) {
                     packageName = mTransport.nextRestorePackage();
+
                     if (packageName == null) {
-                        // STOPSHIP TODO: Handle the failure somehow?
                         Log.e(TAG, "Error getting next restore package");
+                        EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
                         return;
                     } else if (packageName.equals("")) {
                         break;
                     }
 
                     if (mObserver != null) {
-                        ++count;
                         try {
                             mObserver.onUpdate(count);
                         } catch (RemoteException e) {
@@ -1156,21 +1181,34 @@
                     Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
                     if (metaInfo == null) {
                         Log.e(TAG, "Missing metadata for " + packageName);
+                        EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
+                                "Package metadata missing");
                         continue;
                     }
 
-                    int flags = PackageManager.GET_SIGNATURES;
-                    PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags);
+                    PackageInfo packageInfo;
+                    try {
+                        int flags = PackageManager.GET_SIGNATURES;
+                        packageInfo = mPackageManager.getPackageInfo(packageName, flags);
+                    } catch (NameNotFoundException e) {
+                        Log.e(TAG, "Invalid package restoring data", e);
+                        EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
+                                "Package missing on device");
+                        continue;
+                    }
+
                     if (metaInfo.versionCode > packageInfo.versionCode) {
-                        Log.w(TAG, "Package " + packageName
-                                + " restore version [" + metaInfo.versionCode
-                                + "] is too new for installed version ["
-                                + packageInfo.versionCode + "]");
+                        String message = "Version " + metaInfo.versionCode
+                                + " > installed version " + packageInfo.versionCode;
+                        Log.w(TAG, "Package " + packageName + ": " + message);
+                        EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName, message);
                         continue;
                     }
 
                     if (!signaturesMatch(metaInfo.signatures, packageInfo.signatures)) {
                         Log.w(TAG, "Signature mismatch restoring " + packageName);
+                        EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
+                                "Signature mismatch");
                         continue;
                     }
 
@@ -1186,11 +1224,14 @@
                             IApplicationThread.BACKUP_MODE_RESTORE);
                     if (agent == null) {
                         Log.w(TAG, "Can't find backup agent for " + packageName);
+                        EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
+                                "Restore agent missing");
                         continue;
                     }
 
                     try {
                         processOneRestore(packageInfo, metaInfo.versionCode, agent);
+                        ++count;
                     } finally {
                         // unbind even on timeout or failure, just in case
                         mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
@@ -1199,21 +1240,19 @@
 
                 // if we get this far, report success to the observer
                 error = 0;
-            } catch (NameNotFoundException e) {
-                // STOPSHIP TODO: Handle the failure somehow?
-                Log.e(TAG, "Invalid paackage restoring data", e);
-            } catch (RemoteException e) {
-                // STOPSHIP TODO: Handle the failure somehow?
-                Log.e(TAG, "Error restoring data", e);
+                int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
+                EventLog.writeEvent(RESTORE_SUCCESS_EVENT, count, millis);
+            } catch (Exception e) {
+                Log.e(TAG, "Error in restore thread", e);
             } finally {
+                if (DEBUG) Log.d(TAG, "finishing restore mObserver=" + mObserver);
+
                 try {
                     mTransport.finishRestore();
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error finishing restore", e);
                 }
 
-                Log.d(TAG, "finishing restore mObserver=" + mObserver);
-
                 if (mObserver != null) {
                     try {
                         mObserver.restoreFinished(error);
@@ -1232,57 +1271,58 @@
             // !!! TODO: actually run the restore through mTransport
             final String packageName = app.packageName;
 
-            Log.d(TAG, "processOneRestore packageName=" + packageName);
+            if (DEBUG) Log.d(TAG, "processOneRestore packageName=" + packageName);
 
             // !!! TODO: get the dirs from the transport
             File backupDataName = new File(mDataDir, packageName + ".restore");
-            backupDataName.delete();
-            try {
-                ParcelFileDescriptor backupData =
-                    ParcelFileDescriptor.open(backupDataName,
-                            ParcelFileDescriptor.MODE_READ_WRITE |
-                            ParcelFileDescriptor.MODE_CREATE);
+            File newStateName = new File(mStateDir, packageName + ".new");
+            File savedStateName = new File(mStateDir, packageName);
 
+            ParcelFileDescriptor backupData = null;
+            ParcelFileDescriptor newState = null;
+
+            try {
                 // Run the transport's restore pass
-                // Run the target's backup pass
-                try {
-                    if (!mTransport.getRestoreData(backupData)) {
-                        // STOPSHIP TODO: Handle this error somehow?
-                        Log.e(TAG, "Error getting restore data for " + packageName);
-                        return;
-                    }
-                } finally {
-                    backupData.close();
+                backupData = ParcelFileDescriptor.open(backupDataName,
+                            ParcelFileDescriptor.MODE_READ_WRITE |
+                            ParcelFileDescriptor.MODE_CREATE |
+                            ParcelFileDescriptor.MODE_TRUNCATE);
+
+                if (!mTransport.getRestoreData(backupData)) {
+                    Log.e(TAG, "Error getting restore data for " + packageName);
+                    EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
+                    return;
                 }
 
                 // Okay, we have the data.  Now have the agent do the restore.
-                File newStateName = new File(mStateDir, packageName + ".new");
-                ParcelFileDescriptor newState =
-                    ParcelFileDescriptor.open(newStateName,
-                            ParcelFileDescriptor.MODE_READ_WRITE |
-                            ParcelFileDescriptor.MODE_CREATE);
-
+                backupData.close();
                 backupData = ParcelFileDescriptor.open(backupDataName,
                             ParcelFileDescriptor.MODE_READ_ONLY);
 
-                try {
-                    agent.doRestore(backupData, appVersionCode, newState);
-                } finally {
-                    newState.close();
-                    backupData.close();
-                }
+                newState = ParcelFileDescriptor.open(newStateName,
+                            ParcelFileDescriptor.MODE_READ_WRITE |
+                            ParcelFileDescriptor.MODE_CREATE |
+                            ParcelFileDescriptor.MODE_TRUNCATE);
+
+                agent.doRestore(backupData, appVersionCode, newState);
 
                 // if everything went okay, remember the recorded state now
-                File savedStateName = new File(mStateDir, packageName);
                 newStateName.renameTo(savedStateName);
+                int size = (int) backupDataName.length();
+                EventLog.writeEvent(RESTORE_PACKAGE_EVENT, packageName, size);
             } catch (Exception e) {
+                Log.e(TAG, "Error restoring data for " + packageName, e);
+                EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName, e.toString());
+
                 // If the agent fails restore, it might have put the app's data
                 // into an incoherent state.  For consistency we wipe its data
                 // again in this case before propagating the exception
-                Log.e(TAG, "Error restoring data for " + packageName, e);
                 clearApplicationDataSynchronous(packageName);
             } finally {
                 backupDataName.delete();
+                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
+                try { if (newState != null) newState.close(); } catch (IOException e) {}
+                backupData = newState = null;
             }
         }
     }
@@ -1328,6 +1368,7 @@
         // Record that we need a backup pass for the caller.  Since multiple callers
         // may share a uid, we need to note all candidates within that uid and schedule
         // a backup pass for each of them.
+        EventLog.writeEvent(BACKUP_DATA_CHANGED_EVENT, packageName);
 
         // If the caller does not hold the BACKUP permission, it can only request a
         // backup of its own data.
@@ -1625,57 +1666,66 @@
         }
 
         // --- Binder interface ---
-        public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
+        public synchronized RestoreSet[] getAvailableRestoreSets() {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                     "getAvailableRestoreSets");
 
             try {
-            synchronized(this) {
                 if (mRestoreTransport == null) {
                     Log.w(TAG, "Null transport getting restore sets");
-                } else if (mRestoreSets == null) { // valid transport; do the one-time fetch
+                    return null;
+                }
+                if (mRestoreSets == null) { // valid transport; do the one-time fetch
                     mRestoreSets = mRestoreTransport.getAvailableRestoreSets();
+                    if (mRestoreSets == null) EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
                 }
                 return mRestoreSets;
-            }
-            } catch (RuntimeException e) {
-                Log.d(TAG, "getAvailableRestoreSets exception");
-                e.printStackTrace();
-                throw e;
+            } catch (Exception e) {
+                Log.e(TAG, "Error in getAvailableRestoreSets", e);
+                return null;
             }
         }
 
-        public int performRestore(long token, IRestoreObserver observer)
-                throws android.os.RemoteException {
-            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "performRestore");
+        public synchronized int performRestore(long token, IRestoreObserver observer) {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                    "performRestore");
 
-            Log.d(TAG, "performRestore token=" + token + " observer=" + observer);
+            if (DEBUG) Log.d(TAG, "performRestore token=" + token + " observer=" + observer);
 
-            if (mRestoreSets != null) {
-                for (int i = 0; i < mRestoreSets.length; i++) {
-                    if (token == mRestoreSets[i].token) {
-                        mWakelock.acquire();
-                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
-                        msg.obj = new RestoreParams(mRestoreTransport, observer, token);
-                        mBackupHandler.sendMessage(msg);
-                        return 0;
-                    }
+            if (mRestoreTransport == null || mRestoreSets == null) {
+                Log.e(TAG, "Ignoring performRestore() with no restore set");
+                return -1;
+            }
+
+            for (int i = 0; i < mRestoreSets.length; i++) {
+                if (token == mRestoreSets[i].token) {
+                    mWakelock.acquire();
+                    Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+                    msg.obj = new RestoreParams(mRestoreTransport, observer, token);
+                    mBackupHandler.sendMessage(msg);
+                    return 0;
                 }
-            } else {
-                if (DEBUG) Log.v(TAG, "No current restore set, not doing restore");
             }
             return -1;
         }
 
-        public void endRestoreSession() throws android.os.RemoteException {
+        public synchronized void endRestoreSession() {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                     "endRestoreSession");
 
-            Log.d(TAG, "endRestoreSession");
+            if (DEBUG) Log.d(TAG, "endRestoreSession");
 
-            mRestoreTransport.finishRestore();
-            mRestoreTransport = null;
-            synchronized(BackupManagerService.this) {
+            synchronized (this) {
+                try {
+                    if (mRestoreTransport != null) mRestoreTransport.finishRestore();
+                } catch (Exception e) {
+                    Log.e(TAG, "Error in finishRestore", e);
+                } finally {
+                    mRestoreTransport = null;
+                }
+            }
+
+            synchronized (BackupManagerService.this) {
                 if (BackupManagerService.this.mActiveRestoreSession == this) {
                     BackupManagerService.this.mActiveRestoreSession = null;
                 } else {
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index a682fcb..45c1e5c 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -44,6 +44,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 
+import com.android.internal.app.ShutdownThread;
 
 
 /**
@@ -182,6 +183,11 @@
 
         boolean logOutlier = false;
         long dischargeDuration = 0;
+
+        // shut down gracefully if our battery is critically low and we are not powered
+        if (mBatteryLevel == 0 && isPowered(0xffffffff)) {
+            ShutdownThread.shutdown(mContext, false);
+        }
         
         mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL;
         if (mAcOnline) {
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index d6cd4ef..134fb6f 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -60,7 +60,6 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Debug;
 import android.os.HandlerThread;
 import android.os.Parcel;
 import android.os.RemoteException;
@@ -1829,6 +1828,11 @@
             ps = mSettings.peekPackageLP(pkg.packageName);
             updatedPkg = mSettings.mDisabledSysPackages.get(pkg.packageName);
         }
+        // Verify certificates first
+        if (!collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)) {
+            Log.i(TAG, "Failed verifying certificates for package:" + pkg.packageName);
+            return null;
+        }
         if (updatedPkg != null) {
             // An updated system app will not have the PARSE_IS_SYSTEM flag set initially
             parseFlags |= PackageParser.PARSE_IS_SYSTEM;
@@ -1846,16 +1850,19 @@
                         return null;
                     } else {
                         // Delete the older apk pointed to by ps
+                        // At this point, its safely assumed that package installation for
+                        // apps in system partition will go through. If not there won't be a working
+                        // version of the app
+                        synchronized (mPackages) {
+                            // Just remove the loaded entries from package lists.
+                            mPackages.remove(ps.name);
+                        }
                         deletePackageResourcesLI(ps.name, ps.codePathString, ps.resourcePathString);
                         mSettings.enableSystemPackageLP(ps.name);
                     }
                 }
             }
         }
-        if (!collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)) {
-            Log.i(TAG, "Failed verifying certificates for package:" + pkg.packageName);
-            return null;
-        }
         // The apk is forward locked (not public) if its code and resources
         // are kept in different files.
         if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
@@ -2135,7 +2142,7 @@
                 pkg.applicationInfo.packageName,
                 pkg.applicationInfo.processName,
                 pkg.applicationInfo.uid);
-        pkg.applicationInfo.publicSourceDir = pkgSetting.resourcePathString;
+        pkg.applicationInfo.publicSourceDir = destResourceFile.toString();
 
         File dataPath;
         if (mPlatformPackage == pkg) {
@@ -2262,15 +2269,26 @@
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
         }
 
+        // We don't expect installation to fail beyond this point,
         if ((scanMode&SCAN_MONITOR) != 0) {
             pkg.mPath = destCodeFile.getAbsolutePath();
             mAppDirs.put(pkg.mPath, pkg);
         }
 
+        // Request the ActivityManager to kill the process(only for existing packages)
+        // so that we do not end up in a confused state while the user is still using the older
+        // version of the application while the new one gets installed.
+        IActivityManager am = ActivityManagerNative.getDefault();
+        if ((am != null) && ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING ) != 0)) {
+            try {
+                am.killApplicationWithUid(pkg.applicationInfo.packageName,
+                        pkg.applicationInfo.uid);
+            } catch (RemoteException e) {
+            }
+        }
         synchronized (mPackages) {
-            // We don't expect installation to fail beyond this point
             // Add the new setting to mSettings
-            mSettings.insertPackageSettingLP(pkgSetting, pkg.packageName, suid);
+            mSettings.insertPackageSettingLP(pkgSetting, pkg, destCodeFile, destResourceFile);
             // Add the new setting to mPackages
             mPackages.put(pkg.applicationInfo.packageName, pkg);
             int N = pkg.providers.size();
@@ -2958,7 +2976,8 @@
         }
         
         if ((addedPermission || replace) && !ps.permissionsFixed &&
-                (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
+                ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) ||
+                ((ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)){
             // This is the first that we have heard about this package, so the
             // permissions we have now selected are fixed until explicitly
             // changed.
@@ -4159,7 +4178,9 @@
     private void removePackageDataLI(PackageParser.Package p, PackageRemovedInfo outInfo, 
             int flags) {
         String packageName = p.packageName;
-        outInfo.removedPackage = packageName;
+        if (outInfo != null) {
+            outInfo.removedPackage = packageName;
+        }
         removePackageLI(p, true);
         // Retrieve object to delete permissions for shared user later on
         PackageSetting deletedPs;
@@ -4181,7 +4202,9 @@
                 dataDir.delete();
             }
             synchronized (mPackages) {
-                outInfo.removedUid = mSettings.removePackageLP(packageName);                
+                if (outInfo != null) {
+                    outInfo.removedUid = mSettings.removePackageLP(packageName);
+                }
             }
         }
         synchronized (mPackages) {
@@ -4256,7 +4279,7 @@
         }
         return true;
     }
-    
+
     private void deletePackageResourcesLI(String packageName,
             String sourceDir, String publicSourceDir) {
         File sourceFile = new File(sourceDir);
@@ -4286,7 +4309,9 @@
             Log.w(TAG, "Package " + p.packageName + " has no applicationInfo.");
             return false;
         }
-        outInfo.uid = applicationInfo.uid;
+        if (outInfo != null) {
+            outInfo.uid = applicationInfo.uid;
+        }
 
         // Delete package data from internal structures and also remove data if flag is set
         removePackageDataLI(p, outInfo, flags);
@@ -5463,7 +5488,7 @@
         String resourcePathString;
         private long timeStamp;
         private String timeStampString = "0";
-        final int versionCode;
+        int versionCode;
 
         PackageSignatures signatures = new PackageSignatures();
 
@@ -5701,10 +5726,6 @@
             final String name = pkg.packageName;
             PackageSetting p = getPackageLP(name, sharedUser, codePath,
                     resourcePath, pkg.mVersionCode, pkgFlags, create, add);
-
-            if (p != null) {
-                p.pkg = pkg;
-            }
             return p;
         }
         
@@ -5852,22 +5873,18 @@
             if (p != null) {
                 if (!p.codePath.equals(codePath)) {
                     // Check to see if its a disabled system app
-                    PackageSetting ps = mDisabledSysPackages.get(name);
-                    if((ps != null) && ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
+                    if((p != null) && ((p.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
                         // This is an updated system app with versions in both system
                         // and data partition. Just let the most recent version
                         // take precedence.
-                        return p;
+                        Log.w(TAG, "Trying to update system app code path from " +
+                                p.codePathString + " to " + codePath.toString());
                     } else {
                         // Let the app continue with previous uid if code path changes.
                         reportSettingsProblem(Log.WARN,
                                 "Package " + name + " codePath changed from " + p.codePath
                                 + " to " + codePath + "; Retaining data and using new code from " +
                                 codePath);
-                        p.codePath = codePath;
-                        p.resourcePath = resourcePath;
-                        p.codePathString = codePath.toString();
-                        p.resourcePathString = resourcePath.toString();
                     }
                 } else if (p.sharedUser != sharedUser) {
                     reportSettingsProblem(Log.WARN,
@@ -5891,8 +5908,29 @@
                 if (sharedUser != null) {
                     p.userId = sharedUser.userId;
                 } else if (MULTIPLE_APPLICATION_UIDS) {
-                    // Assign new user id
-                    p.userId = newUserIdLP(p);
+                    // Clone the setting here for disabled system packages
+                    PackageSetting dis = mDisabledSysPackages.get(name);
+                    if (dis != null) {
+                        // For disabled packages a new setting is created
+                        // from the existing user id. This still has to be
+                        // added to list of user id's
+                        // Copy signatures from previous setting
+                        if (dis.signatures.mSignatures != null) {
+                            p.signatures.mSignatures = dis.signatures.mSignatures.clone();
+                        }
+                        p.userId = dis.userId;
+                        // Clone permissions
+                        p.grantedPermissions = new HashSet<String>(dis.grantedPermissions);
+                        p.loadedPermissions = new HashSet<String>(dis.loadedPermissions);
+                        // Clone component info
+                        p.disabledComponents = new HashSet<String>(dis.disabledComponents);
+                        p.enabledComponents = new HashSet<String>(dis.enabledComponents);
+                        // Add new setting to list of user ids
+                        addUserIdLP(p.userId, p, name);
+                    } else {
+                        // Assign new user id
+                        p.userId = newUserIdLP(p);
+                    }
                 } else {
                     p.userId = FIRST_APPLICATION_UID;
                 }
@@ -5904,15 +5942,39 @@
                 if (add) {
                     // Finish adding new package by adding it and updating shared 
                     // user preferences
-                    insertPackageSettingLP(p, name, sharedUser);
+                    addPackageSettingLP(p, name, sharedUser);
                 }
             }
             return p;
         }
-        
+
+        private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg,
+                File codePath, File resourcePath) {
+            p.pkg = pkg;
+            // Update code path if needed
+            if (!codePath.toString().equalsIgnoreCase(p.codePathString)) {
+                Log.w(TAG, "Code path for pkg : " + p.pkg.packageName +
+                        " changing form " + p.codePathString + " to " + codePath);
+                p.codePath = codePath;
+                p.codePathString = codePath.toString();
+            }
+            //Update resource path if needed
+            if (!resourcePath.toString().equalsIgnoreCase(p.resourcePathString)) {
+                Log.w(TAG, "Resource path for pkg : " + p.pkg.packageName +
+                        " changing form " + p.resourcePathString + " to " + resourcePath);
+                p.resourcePath = resourcePath;
+                p.resourcePathString = resourcePath.toString();
+            }
+            // Update version code if needed
+             if (pkg.mVersionCode != p.versionCode) {
+                p.versionCode = pkg.mVersionCode;
+            }
+            addPackageSettingLP(p, pkg.packageName, p.sharedUser);
+        }
+
         // Utility method that adds a PackageSetting to mPackages and
         // completes updating the shared user attributes
-        private void insertPackageSettingLP(PackageSetting p, String name,
+        private void addPackageSettingLP(PackageSetting p, String name,
                 SharedUserSetting sharedUser) {
             mPackages.put(name, p);
             if (sharedUser != null) {
@@ -6005,7 +6067,7 @@
                 }
                 if (mUserIds.get(index) != null) {
                     reportSettingsProblem(Log.ERROR,
-                            "Adding duplicate shared id: " + uid
+                            "Adding duplicate user id: " + uid
                             + " name=" + name);
                     return false;
                 }
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 79d78ad1..a3c3436 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -29,6 +29,10 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Handler;
@@ -58,7 +62,8 @@
 import java.util.Observable;
 import java.util.Observer;
 
-class PowerManagerService extends IPowerManager.Stub implements LocalPowerManager, Watchdog.Monitor {
+class PowerManagerService extends IPowerManager.Stub
+        implements LocalPowerManager,Watchdog.Monitor, SensorEventListener {
 
     private static final String TAG = "PowerManagerService";
     static final String PARTIAL_NAME = "PowerManagerService";
@@ -72,7 +77,8 @@
     private static final int LOCK_MASK = PowerManager.PARTIAL_WAKE_LOCK
                                         | PowerManager.SCREEN_DIM_WAKE_LOCK
                                         | PowerManager.SCREEN_BRIGHT_WAKE_LOCK
-                                        | PowerManager.FULL_WAKE_LOCK;
+                                        | PowerManager.FULL_WAKE_LOCK
+                                        | PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
 
     //                       time since last state:               time since last event:
     // The short keylight delay comes from Gservices; this is the default.
@@ -138,6 +144,7 @@
     private int[] mBroadcastQueue = new int[] { -1, -1, -1 };
     private int[] mBroadcastWhy = new int[3];
     private int mPartialCount = 0;
+    private int mProximityCount = 0;
     private int mPowerState;
     private boolean mOffBecauseOfUser;
     private int mUserState;
@@ -175,6 +182,8 @@
     private IActivityManager mActivityService;
     private IBatteryStats mBatteryStats;
     private BatteryService mBatteryService;
+    private SensorManager mSensorManager;
+    private Sensor mProximitySensor;
     private boolean mDimScreen = true;
     private long mNextTimeout;
     private volatile int mPokey = 0;
@@ -536,6 +545,7 @@
                     wl.minState = SCREEN_DIM;
                     break;
                 case PowerManager.PARTIAL_WAKE_LOCK:
+                case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
                     break;
                 default:
                     // just log and bail.  we're in the server, so don't
@@ -583,6 +593,11 @@
                 }
             }
             Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME);
+        } else if ((flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
+            mProximityCount++;
+            if (mProximityCount == 1) {
+                enableProximityLockLocked();
+            }
         }
         if (newlock) {
             acquireUid = wl.uid;
@@ -639,6 +654,11 @@
                 if (LOG_PARTIAL_WL) EventLog.writeEvent(LOG_POWER_PARTIAL_WAKE_STATE, 0, wl.tag);
                 Power.releaseWakeLock(PARTIAL_NAME);
             }
+        } else if ((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
+            mProximityCount--;
+            if (mProximityCount == 0) {
+                disableProximityLockLocked();
+            }
         }
         // Unlink the lock from the binder.
         wl.binder.unlinkToDeath(wl, 0);
@@ -1996,4 +2016,47 @@
     public void monitor() {
         synchronized (mLocks) { }
     }
+
+    public int getSupportedWakeLockFlags() {
+        int result = PowerManager.PARTIAL_WAKE_LOCK
+                   | PowerManager.FULL_WAKE_LOCK
+                   | PowerManager.SCREEN_DIM_WAKE_LOCK;
+
+        // call getSensorManager() to make sure mProximitySensor is initialized
+        getSensorManager();
+        if (mProximitySensor != null) {
+            result |= PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
+        }
+
+        return result;
+    }
+
+    private SensorManager getSensorManager() {
+        if (mSensorManager == null) {
+            mSensorManager = new SensorManager(mHandlerThread.getLooper());
+            mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+        }
+        return mSensorManager;
+    }
+
+    private void enableProximityLockLocked() {
+        mSensorManager.registerListener(this, mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
+    }
+
+    private void disableProximityLockLocked() {
+        mSensorManager.unregisterListener(this);
+    }
+
+    public void onSensorChanged(SensorEvent event) {
+        long milliseconds = event.timestamp / 1000000;
+        if (event.values[0] == 0.0) {
+            goToSleep(milliseconds);
+        } else {
+            userActivity(milliseconds, false);
+        }
+    }
+
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        // ignore
+    }
 }
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 78ca831..edba5b6 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -8231,7 +8231,9 @@
                         // This has changed the visibility of windows, so perform
                         // a new layout to get them all up-to-date.
                         mLayoutNeeded = true;
-                        moveInputMethodWindowsIfNeededLocked(true);
+                        if (!moveInputMethodWindowsIfNeededLocked(true)) {
+                            assignLayersLocked();
+                        }
                         performLayoutLockedInner();
                         updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES);
 
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 8a4b8f9..7bd2532 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -4742,7 +4742,30 @@
             Binder.restoreCallingIdentity(callingId);
         }
     }
-    
+
+    /*
+     * The pkg name and uid have to be specified.
+     * @see android.app.IActivityManager#killApplicationWithUid(java.lang.String, int)
+     */
+    public void killApplicationWithUid(String pkg, int uid) {
+        if (pkg == null) {
+            return;
+        }
+        // Make sure the uid is valid.
+        if (uid < 0) {
+            Log.w(TAG, "Invalid uid specified for pkg : " + pkg);
+            return;
+        }
+        int callerUid = Binder.getCallingUid();
+        // Only the system server can kill an application
+        if (callerUid == Process.SYSTEM_UID) {
+            uninstallPackageLocked(pkg, uid, false);
+        } else {
+            throw new SecurityException(callerUid + " cannot kill pkg: " +
+                    pkg);
+        }
+    }
+
     private void restartPackageLocked(final String packageName, int uid) {
         uninstallPackageLocked(packageName, uid, false);
         Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 775b034..fc491d7 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -278,8 +278,8 @@
     public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) {
         int activePhone = TelephonyManager.getDefault().getPhoneType();
         TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
-            com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly) :
-            com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly);
+            com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly) :
+            com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
         int ret[] = new int[4];
         ret[0] = ted.msgCount;
         ret[1] = ted.codeUnitCount;
@@ -299,8 +299,8 @@
     public static ArrayList<String> fragmentText(String text) {
         int activePhone = TelephonyManager.getDefault().getPhoneType();
         TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
-            com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false) :
-            com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false);
+            com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false) :
+            com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false);
 
         // TODO(cleanup): The code here could be rolled into the logic
         // below cleanly if these MAX_* constants were defined more
@@ -321,11 +321,8 @@
         while (pos < textLen) {
             int nextPos = 0;  // Counts code units.
             if (ted.codeUnitSize == ENCODING_7BIT) {
-                if (PHONE_TYPE_CDMA == activePhone) {
-                    nextPos = pos + Math.min(limit, textLen - pos);
-                } else {
-                    nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit);
-                }
+                // For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode).
+                nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit);
             } else {  // Assume unicode.
                 nextPos = pos + Math.min(limit / 2, textLen - pos);
             }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index aec7aee..dda0187 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -1057,6 +1057,7 @@
                         onComplete.sendToTarget();
                     }
                 }
+                break;
 
                 default:{
                     throw new RuntimeException("unexpected event not handled");
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
index cc456c5..7788c75 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -668,8 +668,8 @@
             // the hangup reason is user ignoring or timing out. So conn.onDisconnect()
             // is not called here. Instead, conn.onLocalDisconnect() is called.
             conn.onLocalDisconnect();
-            phone.notifyPreciseCallStateChanged();
             updatePhoneState();
+            phone.notifyPreciseCallStateChanged();
             return;
         } else {
             try {
@@ -957,6 +957,7 @@
                 if (ar.exception == null) {
                     // Assume 3 way call is connected
                     pendingMO.onConnectedInOrOut();
+                    pendingMO = null;
                 }
             break;
 
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 13cea99..c71003b 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -798,23 +798,34 @@
         return null;
    }
 
-    private static void decodeMessageId(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeMessageId(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 3) {
-            throw new CodingException("MESSAGE_IDENTIFIER subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 3 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.messageType = inStream.read(4);
+            bData.messageId = inStream.read(8) << 8;
+            bData.messageId |= inStream.read(8);
+            bData.hasUserDataHeader = (inStream.read(1) == 1);
+            inStream.skip(3);
         }
-        bData.messageType = inStream.read(4);
-        bData.messageId = inStream.read(8) << 8;
-        bData.messageId |= inStream.read(8);
-        bData.hasUserDataHeader = (inStream.read(1) == 1);
-        inStream.skip(3);
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        return decodeSuccess;
     }
 
-    private static void decodeUserData(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeUserData(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException
     {
-        int paramBytes = inStream.read(8);
+        int paramBits = inStream.read(8) * 8;
         bData.userData = new UserData();
         bData.userData.msgEncoding = inStream.read(5);
         bData.userData.msgEncodingSet = true;
@@ -827,8 +838,9 @@
         }
         bData.userData.numFields = inStream.read(8);
         consumedBits += 8;
-        int dataBits = (paramBytes * 8) - consumedBits;
+        int dataBits = paramBits - consumedBits;
         bData.userData.payload = inStream.readByteArray(dataBits);
+        return true;
     }
 
     private static String decodeUtf16(byte[] data, int offset, int numFields)
@@ -1081,36 +1093,68 @@
         }
     }
 
-    private static void decodeReplyOption(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeReplyOption(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        int paramBytes = inStream.read(8);
-        if (paramBytes != 1) {
-            throw new CodingException("REPLY_OPTION subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 1 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.userAckReq     = (inStream.read(1) == 1);
+            bData.deliveryAckReq = (inStream.read(1) == 1);
+            bData.readAckReq     = (inStream.read(1) == 1);
+            bData.reportReq      = (inStream.read(1) == 1);
+            inStream.skip(4);
         }
-        bData.userAckReq     = (inStream.read(1) == 1);
-        bData.deliveryAckReq = (inStream.read(1) == 1);
-        bData.readAckReq     = (inStream.read(1) == 1);
-        bData.reportReq      = (inStream.read(1) == 1);
-        inStream.skip(4);
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "REPLY_OPTION decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        return decodeSuccess;
     }
 
-    private static void decodeMsgCount(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeMsgCount(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 1) {
-            throw new CodingException("NUMBER_OF_MESSAGES subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 1 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.numberOfMessages = inStream.read(8);
         }
-        bData.numberOfMessages = inStream.read(8);
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        return decodeSuccess;
     }
 
-    private static void decodeDepositIndex(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeDepositIndex(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 2) {
-            throw new CodingException("MESSAGE_DEPOSIT_INDEX subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 2 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
         }
-        bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        return decodeSuccess;
     }
 
     private static String decodeDtmfSmsAddress(byte[] rawData, int numFields)
@@ -1144,10 +1188,10 @@
         }
     }
 
-    private static void decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        int paramBytes = inStream.read(8);
+        int paramBits = inStream.read(8) * 8;
         CdmaSmsAddress addr = new CdmaSmsAddress();
         addr.digitMode = inStream.read(1);
         byte fieldBits = 4;
@@ -1160,140 +1204,274 @@
         }
         addr.numberOfDigits = inStream.read(8);
         consumedBits += 8;
-        int remainingBits = (paramBytes * 8) - consumedBits;
+        int remainingBits = paramBits - consumedBits;
         int dataBits = addr.numberOfDigits * fieldBits;
         int paddingBits = remainingBits - dataBits;
         if (remainingBits < dataBits) {
             throw new CodingException("CALLBACK_NUMBER subparam encoding size error (" +
-                                      "remainingBits " + remainingBits + ", dataBits " +
-                                      dataBits + ", paddingBits " + paddingBits + ")");
+                                      "remainingBits + " + remainingBits + ", dataBits + " +
+                                      dataBits + ", paddingBits + " + paddingBits + ")");
         }
         addr.origBytes = inStream.readByteArray(dataBits);
         inStream.skip(paddingBits);
         decodeSmsAddress(addr);
         bData.callbackNumber = addr;
+        return true;
     }
 
-    private static void decodeMsgStatus(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeMsgStatus(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 1) {
-            throw new CodingException("MESSAGE_STATUS subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 1 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.errorClass = inStream.read(2);
+            bData.messageStatus = inStream.read(6);
         }
-        bData.errorClass = inStream.read(2);
-        bData.messageStatus = inStream.read(6);
-        bData.messageStatusSet = true;
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "MESSAGE_STATUS decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        bData.messageStatusSet = decodeSuccess;
+        return decodeSuccess;
     }
 
-    private static void decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 6) {
-            throw new CodingException("MESSAGE_CENTER_TIME_STAMP subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 6 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
         }
-        bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        return decodeSuccess;
     }
 
-    private static void decodeValidityAbs(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeValidityAbs(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 6) {
-            throw new CodingException("VALIDITY_PERIOD_ABSOLUTE subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 6 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
         }
-        bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        return decodeSuccess;
     }
 
-    private static void decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 6) {
-            throw new CodingException("DEFERRED_DELIVERY_TIME_ABSOLUTE subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 6 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(
+                    inStream.readByteArray(6 * 8));
         }
-        bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        return decodeSuccess;
     }
 
-    private static void decodeValidityRel(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeValidityRel(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 1) {
-            throw new CodingException("VALIDITY_PERIOD_RELATIVE subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 1 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.deferredDeliveryTimeRelative = inStream.read(8);
         }
-        bData.deferredDeliveryTimeRelative = inStream.read(8);
-        bData.deferredDeliveryTimeRelativeSet = true;
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        bData.deferredDeliveryTimeRelativeSet = decodeSuccess;
+        return decodeSuccess;
     }
 
-    private static void decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 1) {
-            throw new CodingException("DEFERRED_DELIVERY_TIME_RELATIVE subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 1 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.validityPeriodRelative = inStream.read(8);
         }
-        bData.validityPeriodRelative = inStream.read(8);
-        bData.validityPeriodRelativeSet = true;
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        bData.validityPeriodRelativeSet = decodeSuccess;
+        return decodeSuccess;
     }
 
-    private static void decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 1) {
-            throw new CodingException("PRIVACY_INDICATOR subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 1 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.privacy = inStream.read(2);
+            inStream.skip(6);
         }
-        bData.privacy = inStream.read(2);
-        inStream.skip(6);
-        bData.privacyIndicatorSet = true;
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "PRIVACY_INDICATOR decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        bData.privacyIndicatorSet = decodeSuccess;
+        return decodeSuccess;
     }
 
-    private static void decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 1) {
-            throw new CodingException("LANGUAGE_INDICATOR subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 1 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.language = inStream.read(8);
         }
-        bData.language = inStream.read(8);
-        bData.languageIndicatorSet = true;
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "LANGUAGE_INDICATOR decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        bData.languageIndicatorSet = decodeSuccess;
+        return decodeSuccess;
     }
 
-    private static void decodeDisplayMode(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeDisplayMode(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 1) {
-            throw new CodingException("DISPLAY_MODE subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 1 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.displayMode = inStream.read(2);
+            inStream.skip(6);
         }
-        bData.displayMode = inStream.read(2);
-        inStream.skip(6);
-        bData.displayModeSet = true;
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "DISPLAY_MODE decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        bData.displayModeSet = decodeSuccess;
+        return decodeSuccess;
     }
 
-    private static void decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 1) {
-            throw new CodingException("PRIORITY_INDICATOR subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 1 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.priority = inStream.read(2);
+            inStream.skip(6);
         }
-        bData.priority = inStream.read(2);
-        inStream.skip(6);
-        bData.priorityIndicatorSet = true;
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "PRIORITY_INDICATOR decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        bData.priorityIndicatorSet = decodeSuccess;
+        return decodeSuccess;
     }
 
-    private static void decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 1) {
-            throw new CodingException("ALERT_ON_MESSAGE_DELIVERY subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 1 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.alert = inStream.read(2);
+            inStream.skip(6);
         }
-        bData.alert = inStream.read(2);
-        inStream.skip(6);
-        bData.alertIndicatorSet = true;
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        bData.alertIndicatorSet = decodeSuccess;
+        return decodeSuccess;
     }
 
-    private static void decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream)
+    private static boolean decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
-        if (inStream.read(8) != 1) {
-            throw new CodingException("USER_REPONSE_CODE subparam size incorrect");
+        final int EXPECTED_PARAM_SIZE = 1 * 8;
+        boolean decodeSuccess = false;
+        int paramBits = inStream.read(8) * 8;
+        if (paramBits >= EXPECTED_PARAM_SIZE) {
+            paramBits -= EXPECTED_PARAM_SIZE;
+            decodeSuccess = true;
+            bData.userResponseCode = inStream.read(8);
         }
-        bData.userResponseCode = inStream.read(8);
-        bData.userResponseCodeSet = true;
+        if ((! decodeSuccess) || (paramBits > 0)) {
+            Log.d(LOG_TAG, "USER_REPONSE_CODE decode " +
+                      (decodeSuccess ? "succeeded" : "failed") +
+                      " (extra bits = " + paramBits + ")");
+        }
+        inStream.skip(paramBits);
+        bData.userResponseCodeSet = decodeSuccess;
+        return decodeSuccess;
     }
 
     /**
@@ -1310,72 +1488,73 @@
             BearerData bData = new BearerData();
             int foundSubparamMask = 0;
             while (inStream.available() > 0) {
+                boolean decodeSuccess = false;
                 int subparamId = inStream.read(8);
                 int subparamIdBit = 1 << subparamId;
                 if ((foundSubparamMask & subparamIdBit) != 0) {
                     throw new CodingException("illegal duplicate subparameter (" +
                                               subparamId + ")");
                 }
-                foundSubparamMask |= subparamIdBit;
                 switch (subparamId) {
                 case SUBPARAM_MESSAGE_IDENTIFIER:
-                    decodeMessageId(bData, inStream);
+                    decodeSuccess = decodeMessageId(bData, inStream);
                     break;
                 case SUBPARAM_USER_DATA:
-                    decodeUserData(bData, inStream);
+                    decodeSuccess = decodeUserData(bData, inStream);
                     break;
                 case SUBPARAM_USER_REPONSE_CODE:
-                    decodeUserResponseCode(bData, inStream);
+                    decodeSuccess = decodeUserResponseCode(bData, inStream);
                     break;
                 case SUBPARAM_REPLY_OPTION:
-                    decodeReplyOption(bData, inStream);
+                    decodeSuccess = decodeReplyOption(bData, inStream);
                     break;
                 case SUBPARAM_NUMBER_OF_MESSAGES:
-                    decodeMsgCount(bData, inStream);
+                    decodeSuccess = decodeMsgCount(bData, inStream);
                     break;
                 case SUBPARAM_CALLBACK_NUMBER:
-                    decodeCallbackNumber(bData, inStream);
+                    decodeSuccess = decodeCallbackNumber(bData, inStream);
                     break;
                 case SUBPARAM_MESSAGE_STATUS:
-                    decodeMsgStatus(bData, inStream);
+                    decodeSuccess = decodeMsgStatus(bData, inStream);
                     break;
                 case SUBPARAM_MESSAGE_CENTER_TIME_STAMP:
-                    decodeMsgCenterTimeStamp(bData, inStream);
+                    decodeSuccess = decodeMsgCenterTimeStamp(bData, inStream);
                     break;
                 case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE:
-                    decodeValidityAbs(bData, inStream);
+                    decodeSuccess = decodeValidityAbs(bData, inStream);
                     break;
                 case SUBPARAM_VALIDITY_PERIOD_RELATIVE:
-                    decodeValidityRel(bData, inStream);
+                    decodeSuccess = decodeValidityRel(bData, inStream);
                     break;
                 case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE:
-                    decodeDeferredDeliveryAbs(bData, inStream);
+                    decodeSuccess = decodeDeferredDeliveryAbs(bData, inStream);
                     break;
                 case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE:
-                    decodeDeferredDeliveryRel(bData, inStream);
+                    decodeSuccess = decodeDeferredDeliveryRel(bData, inStream);
                     break;
                 case SUBPARAM_PRIVACY_INDICATOR:
-                    decodePrivacyIndicator(bData, inStream);
+                    decodeSuccess = decodePrivacyIndicator(bData, inStream);
                     break;
                 case SUBPARAM_LANGUAGE_INDICATOR:
-                    decodeLanguageIndicator(bData, inStream);
+                    decodeSuccess = decodeLanguageIndicator(bData, inStream);
                     break;
                 case SUBPARAM_MESSAGE_DISPLAY_MODE:
-                    decodeDisplayMode(bData, inStream);
+                    decodeSuccess = decodeDisplayMode(bData, inStream);
                     break;
                 case SUBPARAM_PRIORITY_INDICATOR:
-                    decodePriorityIndicator(bData, inStream);
+                    decodeSuccess = decodePriorityIndicator(bData, inStream);
                     break;
                 case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY:
-                    decodeMsgDeliveryAlert(bData, inStream);
+                    decodeSuccess = decodeMsgDeliveryAlert(bData, inStream);
                     break;
                 case SUBPARAM_MESSAGE_DEPOSIT_INDEX:
-                    decodeDepositIndex(bData, inStream);
+                    decodeSuccess = decodeDepositIndex(bData, inStream);
                     break;
                 default:
                     throw new CodingException("unsupported bearer data subparameter ("
                                               + subparamId + ")");
                 }
+                if (decodeSuccess) foundSubparamMask |= subparamIdBit;
             }
             if ((foundSubparamMask & (1 << SUBPARAM_MESSAGE_IDENTIFIER)) == 0) {
                 throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
diff --git a/test-runner/android/test/ProviderTestCase2.java b/test-runner/android/test/ProviderTestCase2.java
index ac17ebf..a923d2a 100644
--- a/test-runner/android/test/ProviderTestCase2.java
+++ b/test-runner/android/test/ProviderTestCase2.java
@@ -3,6 +3,7 @@
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.res.Resources;
 import android.test.mock.MockContext;
 import android.test.mock.MockContentResolver;
 import android.database.DatabaseUtils;
@@ -26,6 +27,14 @@
     private IsolatedContext mProviderContext;
     private MockContentResolver mResolver;
 
+       private class MockContext2 extends MockContext {
+
+        @Override
+        public Resources getResources() {
+            return getContext().getResources();
+        }
+    }
+
     public ProviderTestCase2(Class<T> providerClass, String providerAuthority) {
         mProviderClass = providerClass;
         mProviderAuthority = providerAuthority;
@@ -47,7 +56,7 @@
         mResolver = new MockContentResolver();
         final String filenamePrefix = "test.";
         RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
-                new MockContext(), // The context that most methods are delegated to
+                new MockContext2(), // The context that most methods are delegated to
                 getContext(), // The context that file methods are delegated to
                 filenamePrefix);
         mProviderContext = new IsolatedContext(mResolver, targetContextWrapper);
diff --git a/test-runner/android/test/mock/MockContext.java b/test-runner/android/test/mock/MockContext.java
index b83a44d6..9fb1e61 100644
--- a/test-runner/android/test/mock/MockContext.java
+++ b/test-runner/android/test/mock/MockContext.java
@@ -110,6 +110,7 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
     @Override
     public File getSharedPrefsFile(String name) {
         throw new UnsupportedOperationException();
diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
index 90a2917..528545c 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
@@ -637,6 +637,16 @@
         BearerData bd4 = BearerData.decode(HexDump.hexStringToByteArray(pdu4));
         assertEquals(bd4.alert, 3);
         assertEquals(bd4.userData.payloadStr, "Test Alert 3");
+        String pdu5 = "00031000000126114F4CBCFA20DB979F3C39F2A0C9976" +
+            "69ED979794187665E5D1028EFA7A6840E1062D3D39A900C028000";
+        BearerData bd5 = BearerData.decode(HexDump.hexStringToByteArray(pdu5));
+        assertEquals(bd5.alert, BearerData.ALERT_MEDIUM_PRIO);
+        assertEquals(bd5.userData.payloadStr, "test message delivery alert (with 8 bits)");
+        String pdu6 = "00031000000126114F4CBCFA20DB979F3C39F2A0C9976" +
+            "69ED979794187665E5D1028EFA7A6840C1062D3D39A900C00";
+        BearerData bd6 = BearerData.decode(HexDump.hexStringToByteArray(pdu6));
+        assertEquals(bd6.userData.payloadStr, "test message delivery alert (with 0 bits)");
+        assertEquals(bd6.alertIndicatorSet, false);
     }
 
     @SmallTest
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index a03490d..2eecef8 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -16,6 +16,9 @@
 
 package com.android.dumprendertree;
 
+import com.android.dumprendertree.forwarder.AdbUtils;
+import com.android.dumprendertree.forwarder.ForwardServer;
+
 import android.app.Instrumentation;
 import android.content.Intent;
 import android.os.Bundle;
@@ -42,7 +45,7 @@
     private BufferedOutputStream mBufferedOutputFailedStream;
     private BufferedOutputStream mBufferedOutputNoresultStream;
     private BufferedOutputStream mBufferedOutputTimedoutStream;
-    
+
     public void passed(String layout_file) {
         try {
             mBufferedOutputPassedStream.write(layout_file.getBytes());
@@ -52,7 +55,7 @@
             e.printStackTrace();
         }
     }
-    
+
     public void failed(String layout_file) {
         try {
             mBufferedOutputFailedStream.write(layout_file.getBytes());
@@ -62,7 +65,7 @@
             e.printStackTrace();
         }
     }
-    
+
     public void noresult(String layout_file) {
         try {
             mBufferedOutputNoresultStream.write(layout_file.getBytes());
@@ -72,7 +75,7 @@
             e.printStackTrace();
         }
     }
-    
+
     public void timedout(String url) {
         try {
             mBufferedOutputTimedoutStream.write(url.getBytes());
@@ -82,14 +85,14 @@
             e.printStackTrace();
         }
     }
-    
+
     public MyTestRecorder(boolean resume) {
         try {
             File resultsPassedFile = new File("/sdcard/layout_tests_passed.txt");
             File resultsFailedFile = new File("/sdcard/layout_tests_failed.txt");
             File noExpectedResultFile = new File("/sdcard/layout_tests_nontext.txt");
             File resultTimedoutFile = new File("/sdcard/layout_tests_timedout.txt");
-          
+
             mBufferedOutputPassedStream =
                 new BufferedOutputStream(new FileOutputStream(resultsPassedFile, resume));
             mBufferedOutputFailedStream =
@@ -102,7 +105,7 @@
             e.printStackTrace();
         }
     }
-    
+
     public void close() {
         try {
             mBufferedOutputPassedStream.close();
@@ -120,7 +123,7 @@
 
     private static final String LOGTAG = "LayoutTests";
     static final int DEFAULT_TIMEOUT_IN_MILLIS = 5000;
-    
+
     static final String LAYOUT_TESTS_ROOT = "/sdcard/android/layout_tests/";
     static final String LAYOUT_TESTS_RESULT_DIR = "/sdcard/android/layout_tests_results/";
     static final String ANDROID_EXPECTED_RESULT_DIR = "/sdcard/android/expected_results/";
@@ -139,14 +142,35 @@
     static final String LAYOUT_RESULTS_CRASHED_RESULT_FILE = "results/layout_tests_crashed.txt";
     static final String LAYOUT_TESTS_RUNNER = "run_layout_tests.py";
 
+    static final String HTTP_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/";
+    static final String HTTPS_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/ssl/";
+    static final String HTTP_LOCAL_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/local/";
+    static final String HTTP_MEDIA_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/media/";
+    static final String HTTP_WML_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/wml/";
+
+
+    private ForwardServer fs8000, fs8080, fs8443;
+
     private MyTestRecorder mResultRecorder;
     private Vector<String> mTestList;
     private boolean mRebaselineResults;
     private String mTestPathPrefix;
     private boolean mFinished;
-    
+
     public LayoutTestsAutoTest() {
       super("com.android.dumprendertree", TestShellActivity.class);
+
+      int addr = -1;
+      try {
+          addr = AdbUtils.resolve("android-browser-test.mtv.corp.google.com");
+      } catch (IOException ioe) {
+          Log.e(LOGTAG, "failed to resolve server address.", ioe);
+      }
+      if(addr != -1) {
+          fs8000 = new ForwardServer(8000, addr, 8000);
+          fs8080 = new ForwardServer(8080, addr, 8080);
+          fs8443 = new ForwardServer(8443, addr, 8443);
+      }
     }
 
     // This function writes the result of the layout test to
@@ -157,7 +181,7 @@
       bundle.putBoolean(file, result);
       inst.sendStatus(0, bundle);
     }
-    
+
     private void getTestList() {
         // Read test list.
         try {
@@ -174,7 +198,7 @@
             Log.e(LOGTAG, "Error while reading test list : " + e.getMessage());
         }
     }
-  
+
     private void resumeTestList() {
         // read out the test name it stoped last time.
         try {
@@ -189,7 +213,7 @@
             Log.e(LOGTAG, "Error reading " + TEST_STATUS_FILE);
         }
     }
-  
+
     private void clearTestStatus() {
         // Delete TEST_STATUS_FILE
         try {
@@ -208,13 +232,13 @@
         // Write actual results to result directory.
         return shortName.replaceFirst(LAYOUT_TESTS_ROOT, LAYOUT_TESTS_RESULT_DIR) + "-result.txt";
     }
-    
+
     private String getExpectedResultFile(String test) {
         int pos = test.lastIndexOf('.');
         if(pos == -1)
             return null;
         String shortName = test.substring(0, pos);
-        return shortName + "-expected.txt";          
+        return shortName + "-expected.txt";
     }
 
     private String getAndroidExpectedResultFile(String expectedResultFile) {
@@ -224,7 +248,7 @@
     // Wrap up
     private void failedCase(String file) {
         Log.w("Layout test: ", file + " failed");
-        mResultRecorder.failed(file);    
+        mResultRecorder.failed(file);
     }
 
     private void passedCase(String file) {
@@ -236,7 +260,7 @@
         Log.v("Layout test:", file + " no expected result");
         mResultRecorder.noresult(file);
     }
-     
+
     private void processResult(String testFile, String actualResultFile, String expectedResultFile) {
         Log.v(LOGTAG, "  Processing result: " + testFile);
 
@@ -257,13 +281,13 @@
                         break;
                     }
                 }
-                
+
                 if (passing) {
                     passedCase(testFile);
                 } else {
                     failedCase(testFile);
                 }
-                
+
                 fe.close();
                 fr.close();
             } catch (FileNotFoundException ex) {
@@ -278,7 +302,7 @@
             noresultCase(testFile);
         }
     }
-    
+
     private void runTestAndWaitUntilDone(TestShellActivity activity, String test, int timeout) {
         activity.setCallback(new TestShellCallback() {
             public void finished() {
@@ -287,7 +311,7 @@
                     LayoutTestsAutoTest.this.notifyAll();
                 }
             }
-            
+
             public void timedOut(String url) {
             }
         });
@@ -306,16 +330,16 @@
 
             resultFile = getAndroidExpectedResultFile(expectedResultFile);
         }
-        
+
         mFinished = false;
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.setClass(activity, TestShellActivity.class);
         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-        intent.putExtra(TestShellActivity.TEST_URL, "file://" + test);
+        intent.putExtra(TestShellActivity.TEST_URL, getTestUrl(test));
         intent.putExtra(TestShellActivity.RESULT_FILE, resultFile);
         intent.putExtra(TestShellActivity.TIMEOUT_IN_MILLIS, timeout);
         activity.startActivity(intent);
-      
+
         // Wait until done.
         synchronized (this) {
             while(!mFinished){
@@ -324,18 +348,18 @@
                 } catch (InterruptedException e) { }
             }
         }
-        
+
         if (!mRebaselineResults) {
             String expectedResultFile = getExpectedResultFile(test);
             File f = new File(expectedResultFile);
             if (!f.exists()) {
                 expectedResultFile = getAndroidExpectedResultFile(expectedResultFile);
             }
-            
+
             processResult(test, resultFile, expectedResultFile);
         }
-    } 
-    
+    }
+
     // Invokes running of layout tests
     // and waits till it has finished running.
     public void executeLayoutTests(boolean resume) {
@@ -348,28 +372,28 @@
         }
 
         this.mTestList = new Vector<String>();
-      
+
         // Read settings
         try {
             this.mTestPathPrefix =
                 (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getCanonicalPath();
-        } catch (IOException e) {  
+        } catch (IOException e) {
             Log.e(LOGTAG, "Cannot find test path prefix: " + e.getMessage());
             return;
         }
-        
+
         this.mRebaselineResults = runner.mRebaseline;
-        
+
         int timeout = runner.mTimeoutInMillis;
         if (timeout <= 0) {
             timeout = DEFAULT_TIMEOUT_IN_MILLIS;
         }
-          
+
         this.mResultRecorder = new MyTestRecorder(resume);
-          
+
         if (!resume)
             clearTestStatus();
-          
+
         getTestList();
         if (resume)
             resumeTestList();
@@ -377,6 +401,15 @@
         TestShellActivity activity = (TestShellActivity) getActivity();
 
         // Run tests.
+        int addr = -1;
+        try{
+            addr = AdbUtils.resolve("android-browser-test.mtv.corp.google.com");
+        } catch (IOException ioe) {
+            Log.w(LOGTAG, "error while resolving test host name", ioe);
+        }
+        if(addr == -1) {
+            Log.w(LOGTAG, "failed to resolve test host. http tests will fail.");
+        }
         for (int i = 0; i < mTestList.size(); i++) {
             String s = mTestList.elementAt(i);
             FsUtils.updateTestStatus(TEST_STATUS_FILE, s);
@@ -385,10 +418,48 @@
         }
 
         FsUtils.updateTestStatus(TEST_STATUS_FILE, "#DONE");
-        
+        if(fs8000 != null)
+            fs8000.stop();
+        if(fs8080 != null)
+            fs8080.stop();
+        if(fs8443 != null)
+            fs8443.stop();
+
         activity.finish();
     }
 
+    private void startForwardServerIfNeeded() {
+        try {
+            if(fs8000 != null)
+                fs8000.start();
+            if(fs8080 != null)
+                fs8080.start();
+            if(fs8443 != null)
+                fs8443.start();
+        } catch (IOException ioe) {
+            Log.w(LOGTAG, "failed to start forwarder. http tests will fail.", ioe);
+        }
+    }
+
+    private String getTestUrl(String path) {
+        String url = null;
+        if (!path.startsWith(HTTP_TESTS_PREFIX)) {
+            url = "file://" + path;
+        } else {
+            startForwardServerIfNeeded();
+            if (path.startsWith(HTTPS_TESTS_PREFIX)) {
+                // still cut the URL after "http/tests/"
+                url = "https://127.0.0.1:8443/" + path.substring(HTTP_TESTS_PREFIX.length());
+            } else if (!path.startsWith(HTTP_LOCAL_TESTS_PREFIX)
+                    && !path.startsWith(HTTP_MEDIA_TESTS_PREFIX)
+                    && !path.startsWith(HTTP_WML_TESTS_PREFIX)) {
+                url = "http://127.0.0.1:8000/" + path.substring(HTTP_TESTS_PREFIX.length());
+            } else {
+                url = "file://" + path;
+            }
+        }
+        return url;
+    }
 
     private String getTestPath() {
         LayoutTestsAutoRunner runner = (LayoutTestsAutoRunner) getInstrumentation();
@@ -403,10 +474,10 @@
             Log.e("LayoutTestsAutoTest", "Cannot get cannonical path " + e.getMessage());
         }
         Log.v("LayoutTestsAutoTest", " Test path : " + test_path);
-        
+
         return test_path;
     }
-    
+
     public void generateTestList() {
         try {
             File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
@@ -431,7 +502,7 @@
         } catch (Exception e) {
             e.printStackTrace();
         }
-        
+
         executeLayoutTests(false);
     }
 
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 48c1e5d..30e1d99 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -531,8 +531,14 @@
         }
 
         @Override
+        public boolean onJsTimeout() {
+            Log.v(LOGTAG, "JavaScript timeout");
+            return false;
+        }
+
+        @Override
         public void onExceededDatabaseQuota(String url_str,
-                String databaseIdentifier, long currentQuota,
+                String databaseIdentifier, long currentQuota, long totalUsedQuota,
                 WebStorage.QuotaUpdater callback) {
             if (mDumpDatabaseCallbacks) {
                 if (mDatabaseCallbackStrings == null) {
@@ -614,6 +620,9 @@
         }
 
         WebSettings settings = webview.getSettings();
+        settings.setAppCacheEnabled(true);
+        settings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
+        settings.setAppCacheMaxSize(Long.MAX_VALUE);
         settings.setJavaScriptEnabled(true);
         settings.setJavaScriptCanOpenWindowsAutomatically(true);
         settings.setSupportMultipleWindows(true);
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java
new file mode 100644
index 0000000..9a3e9c2
--- /dev/null
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java
@@ -0,0 +1,112 @@
+package com.android.dumprendertree.forwarder;
+
+import android.util.Log;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+public class AdbUtils {
+
+    private static final String ADB_OK = "OKAY";
+    private static final int ADB_PORT = 5037;
+    private static final String ADB_HOST = "127.0.0.1";
+    private static final int ADB_RESPONSE_SIZE = 4;
+
+    private static final String LOGTAG = "AdbUtils";
+
+    /**
+     *
+     * Convert integer format IP into xxx.xxx.xxx.xxx format
+     *
+     * @param host IP address in integer format
+     * @return human readable format
+     */
+    public static String convert(int host) {
+        return ((host >> 24) & 0xFF) + "."
+        + ((host >> 16) & 0xFF) + "."
+        + ((host >> 8) & 0xFF) + "."
+        + (host & 0xFF);
+    }
+
+    /**
+     *
+     * Resolve DNS name into IP address
+     *
+     * @param host DNS name
+     * @return IP address in integer format
+     * @throws IOException
+     */
+    public static int resolve(String host)  throws IOException {
+        Socket localSocket = new Socket(ADB_HOST, ADB_PORT);
+        DataInputStream dis = new DataInputStream(localSocket.getInputStream());
+        OutputStream os = localSocket.getOutputStream();
+        int count_read = 0;
+        byte[] buf = new byte[128];
+
+        if (localSocket == null || dis == null || os == null)
+            return -1;
+        String cmd = "dns:" + host;
+
+        if(!sendAdbCmd(dis, os, cmd))
+            return -1;
+
+        count_read = dis.readInt();
+        localSocket.close();
+        return count_read;
+    }
+
+    /**
+     *
+     * Send an ADB command using existing socket connection
+     *
+     * the streams provided must be from a socket connected to adbd already
+     *
+     * @param is input stream of the socket connection
+     * @param os output stream of the socket
+     * @param cmd the adb command to send
+     * @return if adb gave a success response
+     * @throws IOException
+     */
+    public static boolean sendAdbCmd(InputStream is, OutputStream os,
+            String cmd) throws IOException {
+        byte[] buf = new byte[ADB_RESPONSE_SIZE];
+
+        cmd = String.format("%04X", cmd.length()) + cmd;
+        os.write(cmd.getBytes());
+        int read = is.read(buf);
+        if(read != ADB_RESPONSE_SIZE || !ADB_OK.equals(new String(buf))) {
+            Log.w(LOGTAG, "adb cmd faild.");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     *
+     * Get a tcp socket connection to specified IP address and port proxied by adb
+     *
+     * The proxying is transparent, e.g. if a socket is returned, then it can be written to and
+     * read from as if it is directly connected to the target
+     *
+     * @param remoteAddress IP address of the host to connect to
+     * @param remotePort port of the host to connect to
+     * @return a valid Socket instance if successful, null otherwise
+     */
+    public static Socket getForwardedSocket(int remoteAddress, int remotePort) {
+        try {
+            Socket socket = new Socket(ADB_HOST, ADB_PORT);
+            String cmd = "tcp:" + remotePort + ":" + convert(remoteAddress);
+            if(!sendAdbCmd(socket.getInputStream(), socket.getOutputStream(), cmd)) {
+                socket.close();
+                return null;
+            }
+            return socket;
+        } catch (IOException ioe) {
+            Log.w(LOGTAG, "error creating adb socket", ioe);
+            return null;
+        }
+    }
+}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java
new file mode 100644
index 0000000..74e018e
--- /dev/null
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java
@@ -0,0 +1,117 @@
+package com.android.dumprendertree.forwarder;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ *
+ * A port forwarding server. Listens at specified local port and forward the tcp communications to
+ * external host/port via adb networking proxy.
+ *
+ */
+public class ForwardServer {
+
+    private static final String LOGTAG = "ForwardServer";
+
+    private int remotePort;
+    private int remoteAddress;
+    private int localPort;
+    private ServerSocket serverSocket;
+    private boolean started;
+
+    private Set<Forwarder> forwarders;
+
+    public ForwardServer(int localPort, int remoteAddress, int remotePort) {
+        this.localPort = localPort;
+        this.remoteAddress = remoteAddress;
+        this.remotePort = remotePort;
+        started = false;
+        forwarders = new HashSet<Forwarder>();
+    }
+
+    public synchronized void start() throws IOException {
+        if(!started) {
+            serverSocket = new ServerSocket(localPort);
+            Thread serverThread = new Thread(new ServerRunner(serverSocket));
+            serverThread.setName(LOGTAG);
+            serverThread.start();
+            started = true;
+        }
+    }
+
+    public synchronized void stop() {
+        if(started) {
+            synchronized (forwarders) {
+                for(Forwarder forwarder : forwarders)
+                    forwarder.stop();
+                forwarders.clear();
+            }
+            try {
+                serverSocket.close();
+            } catch (IOException ioe) {
+                Log.v(LOGTAG, "exception while closing", ioe);
+            } finally {
+                started = false;
+            }
+        }
+    }
+
+    public synchronized boolean isRunning() {
+        return started;
+    }
+
+    private class ServerRunner implements Runnable {
+
+        private ServerSocket socket;
+
+        public ServerRunner(ServerSocket socket) {
+            this.socket = socket;
+        }
+
+        public void run() {
+            try {
+                while (true) {
+                    Socket localSocket = socket.accept();
+                    Socket remoteSocket = AdbUtils.getForwardedSocket(remoteAddress, remotePort);
+                    if(remoteSocket == null) {
+                        try {
+                            localSocket.close();
+                        } catch (IOException ioe) {
+                            Log.w(LOGTAG, "error while closing socket", ioe);
+                        } finally {
+                            Log.w(LOGTAG, "failed to start forwarding from " + localSocket);
+                        }
+                    } else {
+                        Forwarder forwarder = new Forwarder(localSocket, remoteSocket,
+                                ForwardServer.this);
+                        forwarder.start();
+                    }
+                }
+            } catch (IOException ioe) {
+                return;
+            }
+        }
+    }
+
+    public void register(Forwarder forwarder) {
+        synchronized (forwarders) {
+            if(!forwarders.contains(forwarder)) {
+                forwarders.add(forwarder);
+            }
+        }
+    }
+
+    public void unregister(Forwarder recyclable) {
+        synchronized (forwarders) {
+            if(forwarders.contains(recyclable)) {
+                recyclable.stop();
+                forwarders.remove(recyclable);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java
new file mode 100644
index 0000000..e1e04a7
--- /dev/null
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java
@@ -0,0 +1,92 @@
+package com.android.dumprendertree.forwarder;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ *
+ * Worker class for {@link ForwardServer}. A Forwarder will be created once the ForwardServer
+ * accepts an incoming connection, and it will then forward the incoming/outgoing streams to a
+ * connection already proxied by adb networking (see also {@link AdbUtils}).
+ *
+ */
+public class Forwarder {
+
+    private ForwardServer server;
+    private Socket from, to;
+
+    private static final String LOGTAG = "Forwarder";
+
+    public Forwarder (Socket from, Socket to, ForwardServer server) {
+        this.server = server;
+        this.from = from;
+        this.to = to;
+        server.register(this);
+    }
+
+    public void start() {
+        Thread outgoing = new Thread(new SocketPipe(from, to));
+        Thread incoming = new Thread(new SocketPipe(to, from));
+        outgoing.setName(LOGTAG);
+        incoming.setName(LOGTAG);
+        outgoing.start();
+        incoming.start();
+    }
+
+    public void stop() {
+        shutdown(from);
+        shutdown(to);
+    }
+
+    private void shutdown(Socket socket) {
+        try {
+            socket.shutdownInput();
+        } catch (IOException e) {
+            Log.v(LOGTAG, "Socket#shutdownInput", e);
+        }
+        try {
+            socket.shutdownOutput();
+        } catch (IOException e) {
+            Log.v(LOGTAG, "Socket#shutdownOutput", e);
+        }
+        try {
+            socket.close();
+        } catch (IOException e) {
+            Log.v(LOGTAG, "Socket#close", e);
+        }
+    }
+
+    private class SocketPipe implements Runnable {
+
+        private Socket in, out;
+
+        public SocketPipe(Socket in, Socket out) {
+            this.in = in;
+            this.out = out;
+        }
+
+        public void run() {
+            try {
+                int length;
+                InputStream is = in.getInputStream();
+                OutputStream os = out.getOutputStream();
+                byte[] buffer = new byte[4096];
+                while ((length = is.read(buffer)) > 0) {
+                    os.write(buffer, 0, length);
+                }
+            } catch (IOException ioe) {
+            } finally {
+                server.unregister(Forwarder.this);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "SocketPipe{" + in + "=>" + out  + "}";
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 12abce5..083cda3 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -1467,6 +1467,7 @@
     public synchronized boolean restart() {
         if (mRunState == RUN_STATE_STOPPED) {
             mRunState = RUN_STATE_STARTING;
+            resetInterface(true);
             return WifiNative.startDriverCommand();
         } else if (mRunState == RUN_STATE_STOPPING) {
             mRunState = RUN_STATE_STARTING;