Merge "Fix 2797185: Integrate 3D RecentApps View into system."
diff --git a/api/current.xml b/api/current.xml
index cbbf3d1..ebb8b3e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -19870,6 +19870,17 @@
  visibility="public"
 >
 </method>
+<method name="isRunning"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="removeAllListeners"
  return="void"
  abstract="false"
@@ -19966,6 +19977,40 @@
 </parameter>
 </method>
 </interface>
+<class name="AnimatableInflater"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AnimatableInflater"
+ type="android.animation.AnimatableInflater"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="loadAnimatable"
+ return="android.animation.Animatable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="id" type="int">
+</parameter>
+<exception name="Resources.NotFoundException" type="android.content.res.Resources.NotFoundException">
+</exception>
+</method>
+</class>
 <class name="AnimatableListenerAdapter"
  extends="java.lang.Object"
  abstract="true"
@@ -20052,10 +20097,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="attrs" type="android.util.AttributeSet">
-</parameter>
 </constructor>
 <constructor name="Animator"
  type="android.animation.Animator"
@@ -20231,6 +20272,19 @@
 <parameter name="playTime" type="long">
 </parameter>
 </method>
+<method name="setDuration"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="duration" type="long">
+</parameter>
+</method>
 <method name="setEvaluator"
  return="void"
  abstract="false"
@@ -20653,10 +20707,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="attrs" type="android.util.AttributeSet">
-</parameter>
 </constructor>
 <constructor name="PropertyAnimator"
  type="android.animation.PropertyAnimator"
@@ -20929,6 +20979,17 @@
  visibility="public"
 >
 </method>
+<method name="isRunning"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="play"
  return="android.animation.Sequencer.Builder"
  abstract="false"
@@ -51169,6 +51230,17 @@
 <parameter name="key" type="java.lang.String">
 </parameter>
 </method>
+<method name="startCommit"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 </interface>
 <interface name="SharedPreferences.OnSharedPreferenceChangeListener"
  abstract="true"
@@ -205789,23 +205861,6 @@
 <exception name="Resources.NotFoundException" type="android.content.res.Resources.NotFoundException">
 </exception>
 </method>
-<method name="loadAnimator"
- return="android.animation.Animatable"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="id" type="int">
-</parameter>
-<exception name="Resources.NotFoundException" type="android.content.res.Resources.NotFoundException">
-</exception>
-</method>
 <method name="loadInterpolator"
  return="android.view.animation.Interpolator"
  abstract="false"
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 0f343ff..4265cc5 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -142,6 +142,11 @@
                 String value = nextArgRequired();
                 intent.putExtra(key, Integer.valueOf(value));
                 hasIntentInfo = true;
+            } else if (opt.equals("--el")) {
+                String key = nextArgRequired();
+                String value = nextArgRequired();
+                intent.putExtra(key, Long.valueOf(value));
+                hasIntentInfo = true;
             } else if (opt.equals("--ez")) {
                 String key = nextArgRequired();
                 String value = nextArgRequired();
@@ -627,6 +632,7 @@
                 "        [--esn <EXTRA_KEY> ...]\n" +
                 "        [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
                 "        [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
+                "        [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
                 "        [-n <COMPONENT>] [-f <FLAGS>]\n" +
                 "        [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
                 "        [--debug-log-resolution]\n" +
diff --git a/cmds/screenshot/screenshot.c b/cmds/screenshot/screenshot.c
index 46e6507..048636c 100644
--- a/cmds/screenshot/screenshot.c
+++ b/cmds/screenshot/screenshot.c
@@ -88,13 +88,61 @@
     png_destroy_write_struct(&png, NULL);
 }
 
+void fork_sound(const char* path) {
+    pid_t pid = fork();
+    if (pid == 0) {
+        execl("/system/bin/stagefright", "stagefright", "-o", "-a", path, NULL);
+    }
+}
+
+void usage() {
+    fprintf(stderr,
+            "usage: screenshot [-s soundfile] filename.png\n"
+            "   -s: play a sound effect to signal success\n"
+            "   -i: autoincrement to avoid overwriting filename.png\n"
+    );
+}
+
 int main(int argc, char**argv) {
     FILE *png = NULL;
     FILE *fb_in = NULL;
-    if (argc < 2) {
-        fprintf(stderr, "usage: screenshot filename.png\n");
-        exit(1);
+    char outfile[PATH_MAX] = "";
+
+    char * soundfile = NULL;
+    int do_increment = 0;
+
+    int c;
+    while ((c = getopt(argc, argv, "s:i")) != -1) {
+        switch (c) {
+            case 's': soundfile = optarg; break;
+            case 'i': do_increment = 1; break;
+            case '?':
+            case 'h':
+                usage(); exit(1);
+        }
     }
+    argc -= optind;
+    argv += optind;
+
+    if (argc < 1) {
+        usage(); exit(1);
+    }
+
+    strlcpy(outfile, argv[0], PATH_MAX);
+    if (do_increment) {
+        struct stat st;
+        char base[PATH_MAX] = "";
+        int i = 0;
+        while (stat(outfile, &st) == 0) {
+            if (!base[0]) {
+                char *p = strrchr(outfile, '.');
+                if (p) *p = '\0';
+                strcpy(base, outfile);
+            }
+            snprintf(outfile, PATH_MAX, "%s-%d.png", base, ++i);
+        }
+    }
+
     fb_in = fopen("/dev/graphics/fb0", "r");
     if (!fb_in) {
         fprintf(stderr, "error: could not read framebuffer\n");
@@ -106,13 +154,18 @@
     setgroups(sizeof(groups)/sizeof(groups[0]), groups);
     setuid(AID_SHELL);
 
-    png = fopen(argv[1], "w");
+    png = fopen(outfile, "w");
     if (!png) {
-        fprintf(stderr, "error: writing file %s: %s\n", argv[1], strerror(errno));
+        fprintf(stderr, "error: writing file %s: %s\n",
+                outfile, strerror(errno));
         exit(1);
     }
 
     take_screenshot(fb_in, png);
 
+    if (soundfile) {
+        fork_sound(soundfile);
+    }
+
     exit(0);
 }
diff --git a/core/java/android/animation/Animatable.java b/core/java/android/animation/Animatable.java
index a49ca8f..d6cf7c0 100644
--- a/core/java/android/animation/Animatable.java
+++ b/core/java/android/animation/Animatable.java
@@ -56,6 +56,13 @@
     public void end() {
     }
 
+
+    /**
+     * Returns whether this Animatable is currently running (having been started and not yet ended).
+     * @return Whether the Animatable is running.
+     */
+    public abstract boolean isRunning();
+
     /**
      * Adds a listener to the set of listeners that are sent events through the life of an
      * animation, such as start, repeat, and end.
diff --git a/core/java/android/animation/AnimatableInflater.java b/core/java/android/animation/AnimatableInflater.java
new file mode 100644
index 0000000..88fa77e
--- /dev/null
+++ b/core/java/android/animation/AnimatableInflater.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.animation;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.content.res.Resources.NotFoundException;
+import android.util.AttributeSet;
+import android.util.Xml;
+import android.view.animation.AnimationUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * This class is used to instantiate menu XML files into Animatable objects.
+ * <p>
+ * For performance reasons, menu inflation relies heavily on pre-processing of
+ * XML files that is done at build time. Therefore, it is not currently possible
+ * to use MenuInflater with an XmlPullParser over a plain XML file at runtime;
+ * it only works with an XmlPullParser returned from a compiled resource (R.
+ * <em>something</em> file.)
+ */
+public class AnimatableInflater {
+
+    /**
+     * These flags are used when parsing Sequencer objects
+     */
+    private static final int TOGETHER = 0;
+    private static final int SEQUENTIALLY = 1;
+
+    /**
+     * Enum values used in XML attributes to indicate the value for mValueType
+     */
+    private static final int VALUE_TYPE_FLOAT       = 0;
+    private static final int VALUE_TYPE_INT         = 1;
+    private static final int VALUE_TYPE_DOUBLE      = 2;
+    private static final int VALUE_TYPE_COLOR       = 3;
+    private static final int VALUE_TYPE_CUSTOM      = 4;
+
+    /**
+     * Loads an {@link Animatable} object from a resource
+     *
+     * @param context Application context used to access resources
+     * @param id The resource id of the animation to load
+     * @return The animatable object reference by the specified id
+     * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
+     */
+    public static Animatable loadAnimatable(Context context, int id)
+            throws NotFoundException {
+
+        XmlResourceParser parser = null;
+        try {
+            parser = context.getResources().getAnimation(id);
+            return createAnimatableFromXml(context, parser);
+        } catch (XmlPullParserException ex) {
+            Resources.NotFoundException rnf =
+                    new Resources.NotFoundException("Can't load animation resource ID #0x" +
+                    Integer.toHexString(id));
+            rnf.initCause(ex);
+            throw rnf;
+        } catch (IOException ex) {
+            Resources.NotFoundException rnf =
+                    new Resources.NotFoundException("Can't load animation resource ID #0x" +
+                    Integer.toHexString(id));
+            rnf.initCause(ex);
+            throw rnf;
+        } finally {
+            if (parser != null) parser.close();
+        }
+    }
+
+    private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+
+        return createAnimatableFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
+    }
+
+    private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser,
+            AttributeSet attrs, Sequencer parent, int sequenceOrdering)
+            throws XmlPullParserException, IOException {
+
+        Animatable anim = null;
+        ArrayList<Animatable> childAnims = null;
+
+        // Make sure we are on a start tag.
+        int type;
+        int depth = parser.getDepth();
+
+        while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+               && type != XmlPullParser.END_DOCUMENT) {
+
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String  name = parser.getName();
+
+            if (name.equals("property")) {
+                anim = loadPropertyAnimator(c, attrs);
+            } else if (name.equals("animator")) {
+                anim = loadAnimator(c, attrs, null);
+            } else if (name.equals("sequencer")) {
+                anim = new Sequencer();
+                TypedArray a = c.obtainStyledAttributes(attrs,
+                        com.android.internal.R.styleable.Sequencer);
+                int ordering = a.getInt(com.android.internal.R.styleable.Sequencer_ordering,
+                        TOGETHER);
+                createAnimatableFromXml(c, parser, attrs, (Sequencer) anim,  ordering);
+                a.recycle();
+            } else {
+                throw new RuntimeException("Unknown animator name: " + parser.getName());
+            }
+
+            if (parent != null) {
+                if (childAnims == null) {
+                    childAnims = new ArrayList<Animatable>();
+                }
+                childAnims.add(anim);
+            }
+        }
+        if (parent != null && childAnims != null) {
+            Animatable[] animsArray = new Animatable[childAnims.size()];
+            int index = 0;
+            for (Animatable a : childAnims) {
+                animsArray[index++] = a;
+            }
+            if (sequenceOrdering == TOGETHER) {
+                parent.playTogether(animsArray);
+            } else {
+                parent.playSequentially(animsArray);
+            }
+        }
+
+        return anim;
+
+    }
+
+    private static PropertyAnimator loadPropertyAnimator(Context context, AttributeSet attrs)
+            throws NotFoundException {
+
+        PropertyAnimator anim = new PropertyAnimator();
+
+        loadAnimator(context, attrs, anim);
+
+        TypedArray a =
+                context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.PropertyAnimator);
+
+        String propertyName = a.getString(com.android.internal.R.styleable.PropertyAnimator_propertyName);
+
+        anim.setPropertyName(propertyName);
+
+        a.recycle();
+
+        return anim;
+    }
+
+    /**
+     * Creates a new animation whose parameters come from the specified context and
+     * attributes set.
+     *
+     * @param context the application environment
+     * @param attrs the set of attributes holding the animation parameters
+     */
+    private static Animator loadAnimator(Context context, AttributeSet attrs, Animator anim)
+            throws NotFoundException {
+
+        TypedArray a =
+                context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animator);
+
+        long duration = a.getInt(com.android.internal.R.styleable.Animator_duration, 0);
+
+        long startDelay = a.getInt(com.android.internal.R.styleable.Animator_startOffset, 0);
+
+        int valueType = a.getInt(com.android.internal.R.styleable.Animator_valueType,
+                VALUE_TYPE_FLOAT);
+
+        Object valueFrom = null;
+        Object valueTo = null;
+        TypeEvaluator evaluator = null;
+
+        switch (valueType) {
+            case VALUE_TYPE_FLOAT:
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueFrom)) {
+                    valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
+                }
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueTo)) {
+                    valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
+                }
+                break;
+            case VALUE_TYPE_COLOR:
+                evaluator = new RGBEvaluator();
+                // fall through to pick up values
+            case VALUE_TYPE_INT:
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueFrom)) {
+                    valueFrom = a.getInteger(com.android.internal.R.styleable.Animator_valueFrom, 0);
+                }
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueTo)) {
+                    valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0);
+                }
+                break;
+            case VALUE_TYPE_DOUBLE:
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueFrom)) {
+                    valueFrom = (Double)((Float)(a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f))).doubleValue();
+                }
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueTo)) {
+                    valueTo = (Double)((Float)a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f)).doubleValue();
+                }
+                break;
+            case VALUE_TYPE_CUSTOM:
+                // TODO: How to get an 'Object' value?
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueFrom)) {
+                    valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
+                }
+                if (a.hasValue(com.android.internal.R.styleable.Animator_valueTo)) {
+                    valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
+                }
+                break;
+        }
+
+        if (anim == null) {
+            anim = new Animator(duration, valueFrom, valueTo);
+        } else {
+            anim.setDuration(duration);
+            anim.setValues(valueFrom, valueTo);
+        }
+
+        anim.setStartDelay(startDelay);
+
+        if (a.hasValue(com.android.internal.R.styleable.Animator_repeatCount)) {
+            anim.setRepeatCount(
+                    a.getInt(com.android.internal.R.styleable.Animator_repeatCount, 0));
+        }
+        if (a.hasValue(com.android.internal.R.styleable.Animator_repeatMode)) {
+            anim.setRepeatMode(
+                    a.getInt(com.android.internal.R.styleable.Animator_repeatMode,
+                            Animator.RESTART));
+        }
+        if (evaluator != null) {
+            anim.setEvaluator(evaluator);
+        }
+
+        final int resID =
+                a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
+        if (resID > 0) {
+            anim.setInterpolator(AnimationUtils.loadInterpolator(context, resID));
+        }
+        a.recycle();
+
+        return anim;
+    }
+}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index eca5f0d..717fc0a 100755
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -17,11 +17,9 @@
 package android.animation;
 
 import android.content.Context;
-import android.content.res.TypedArray;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.util.AttributeSet;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
@@ -70,15 +68,6 @@
     private static final int SEEKED     = 4; // Seeked to some time value
 
     /**
-     * Enum values used in XML attributes to indicate the value for mValueType
-     */
-    private static final int VALUE_TYPE_FLOAT       = 0;
-    private static final int VALUE_TYPE_INT         = 1;
-    private static final int VALUE_TYPE_DOUBLE      = 2;
-    private static final int VALUE_TYPE_COLOR       = 3;
-    private static final int VALUE_TYPE_CUSTOM      = 4;
-
-    /**
      * Internal variables
      * NOTE: This object implements the clone() method, making a deep copy of any referenced
      * objects. As other non-trivial fields are added to this class, make sure to add logic
@@ -209,11 +198,6 @@
     HashMap<String, PropertyValuesHolder> mValuesMap;
 
     /**
-     * The type of the values, as determined by the valueFrom/valueTo properties.
-     */
-    Class mValueType;
-
-    /**
      * Public constants
      */
 
@@ -234,76 +218,13 @@
     public static final int INFINITE = -1;
 
     /**
-     * Creates a new animation whose parameters come from the specified context and
-     * attributes set.
-     *
-     * @param context the application environment
-     * @param attrs the set of attributes holding the animation parameters
+     * Creates a new Animator object. This default constructor is primarily for
+     * use internally; the other constructors which take parameters are more generally
+     * useful.
      */
-    public Animator(Context context, AttributeSet attrs) {
-        TypedArray a =
-                context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animator);
-
-        mDuration = (long) a.getInt(com.android.internal.R.styleable.Animator_duration, 0);
-
-        mStartDelay = (long) a.getInt(com.android.internal.R.styleable.Animator_startOffset, 0);
-
-        final int resID =
-                a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
-        if (resID > 0) {
-            setInterpolator(AnimationUtils.loadInterpolator(context, resID));
-        }
-        int valueType = a.getInt(com.android.internal.R.styleable.Animator_valueType,
-                VALUE_TYPE_FLOAT);
-
-        Object valueFrom = null;
-        Object valueTo = null;
-
-        switch (valueType) {
-            case VALUE_TYPE_FLOAT:
-                valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
-                valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
-                mValueType = float.class;
-                break;
-            case VALUE_TYPE_INT:
-                valueFrom = a.getInt(com.android.internal.R.styleable.Animator_valueFrom, 0);
-                valueTo = a.getInt(com.android.internal.R.styleable.Animator_valueTo, 0);
-                mValueType = int.class;
-                break;
-            case VALUE_TYPE_DOUBLE:
-                valueFrom = (double)
-                        a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
-                valueTo = (double)
-                        a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
-                mValueType = double.class;
-                break;
-            case VALUE_TYPE_COLOR:
-                valueFrom = a.getInt(com.android.internal.R.styleable.Animator_valueFrom, 0);
-                valueTo = a.getInt(com.android.internal.R.styleable.Animator_valueTo, 0);
-                setEvaluator(new RGBEvaluator());
-                mValueType = int.class;
-                break;
-            case VALUE_TYPE_CUSTOM:
-                // TODO: How to get an 'Object' value?
-                valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
-                valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
-                mValueType = Object.class;
-                break;
-        }
-
-        PropertyValuesHolder valuesHolder = new PropertyValuesHolder("", valueFrom, valueTo);
-        mValues = new PropertyValuesHolder[1];
-        mValues[0] = valuesHolder;
-        mValuesMap = new HashMap<String, PropertyValuesHolder>(1);
-        mValuesMap.put("", valuesHolder);
-
-        mRepeatCount = a.getInt(com.android.internal.R.styleable.Animator_repeatCount, mRepeatCount);
-        mRepeatMode = a.getInt(com.android.internal.R.styleable.Animator_repeatMode, RESTART);
-
-        a.recycle();
+    public Animator() {
     }
 
-
     /**
      * Constructs an Animator object with the specified duration and set of
      * values. If the values are a set of PropertyValuesHolder objects, then these objects
@@ -373,6 +294,25 @@
         mInitialized = true;
     }
 
+
+    /**
+     * Sets the length of the animation.
+     *
+     * @param duration The length of the animation, in milliseconds.
+     */
+    public void setDuration(long duration) {
+        mDuration = duration;
+    }
+
+    /**
+     * Gets the length of the animation.
+     *
+     * @return The length of the animation, in milliseconds.
+     */
+    public long getDuration() {
+        return mDuration;
+    }
+
     /**
      * Sets the position of the animation to the specified point in time. This time should
      * be between 0 and the total duration of the animation, including any repetition. If
@@ -723,12 +663,13 @@
      * @param playBackwards Whether the Animator should start playing in reverse.
      */
     private void start(boolean playBackwards) {
+        mPlayingBackwards = playBackwards;
         if ((mStartDelay == 0) && (Thread.currentThread() == Looper.getMainLooper().getThread())) {
             // This sets the initial value of the animation, prior to actually starting it running
             setCurrentPlayTime(getCurrentPlayTime());
         }
-        mPlayingBackwards = playBackwards;
         mPlayingState = STOPPED;
+        mStartedDelay = false;
         sPendingAnimations.add(this);
         if (sAnimationHandler == null) {
             sAnimationHandler = new AnimationHandler();
@@ -738,15 +679,6 @@
         sAnimationHandler.sendEmptyMessage(ANIMATION_START);
     }
 
-    /**
-     * Returns the duration that this animation will run for.
-     *
-     * @return The length in time of the animation, in milliseconds.
-     */
-    public long getDuration() {
-        return mDuration;
-    }
-
     @Override
     public void start() {
         start(false);
@@ -768,15 +700,21 @@
 
     @Override
     public void end() {
+        if (!sAnimations.contains(this) &&
+                (Thread.currentThread() == Looper.getMainLooper().getThread())) {
+            // Special case if the animation has not yet started. Set the end value.
+            long endTime = mDuration;
+            if (mRepeatCount > 0) {
+                endTime += mRepeatCount * mDuration;
+            }
+            setCurrentPlayTime(endTime);
+        }
         // Just set the ENDED flag - this causes the animation to end the next time a frame
         // is processed.
         mPlayingState = ENDED;
     }
 
-    /**
-     * Returns whether this Animator is currently running (having been started and not yet ended).
-     * @return Wehther the Animator is running.
-     */
+    @Override
     public boolean isRunning() {
         return mPlayingState == RUNNING;
     }
@@ -806,6 +744,7 @@
      */
     private void endAnimation() {
         sAnimations.remove(this);
+        mPlayingState = STOPPED;
         if (mListeners != null) {
             ArrayList<AnimatableListener> tmpListeners =
                     (ArrayList<AnimatableListener>) mListeners.clone();
@@ -813,7 +752,6 @@
                 listener.onAnimationEnd(this);
             }
         }
-        mPlayingState = STOPPED;
     }
 
     /**
diff --git a/core/java/android/animation/PropertyAnimator.java b/core/java/android/animation/PropertyAnimator.java
index 022ca7d..8a6edcc 100644
--- a/core/java/android/animation/PropertyAnimator.java
+++ b/core/java/android/animation/PropertyAnimator.java
@@ -16,9 +16,6 @@
 
 package android.animation;
 
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
 import android.util.Log;
 
 import java.lang.reflect.Method;
@@ -81,25 +78,6 @@
     }
 
     /**
-     * Creates a new animation whose parameters come from the specified context and
-     * attributes set.
-     *
-     * @param context the application environment
-     * @param attrs the set of attributes holding the animation parameters
-     */
-    public PropertyAnimator(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                com.android.internal.R.styleable.PropertyAnimator);
-
-        setPropertyName(a.getString(
-                com.android.internal.R.styleable.PropertyAnimator_propertyName));
-
-
-        a.recycle();
-    }
-    /**
      * Determine the setter or getter function using the JavaBeans convention of setFoo or
      * getFoo for a property named 'foo'. This function figures out what the name of the
      * function should be and uses reflection to find the Method with that name on the
@@ -130,6 +108,14 @@
     }
 
     /**
+     * Creates a new PropertyAnimator object. This default constructor is primarily for
+     * use internally; the other constructors which take parameters are more generally
+     * useful.
+     */
+    public PropertyAnimator() {
+    }
+
+    /**
      * A constructor that takes a single property name and set of values. This constructor is
      * used in the simple case of animating a single property.
      *
diff --git a/core/java/android/animation/Sequencer.java b/core/java/android/animation/Sequencer.java
index 7749429..63ec940 100644
--- a/core/java/android/animation/Sequencer.java
+++ b/core/java/android/animation/Sequencer.java
@@ -233,6 +233,10 @@
     @Override
     public void end() {
         mCanceled = true;
+        if (mSortedNodes.size() != mNodes.size()) {
+            // hasn't been started yet - sort the nodes now, then end them
+            sortNodes();
+        }
         if (mSortedNodes.size() > 0) {
             for (Node node : mSortedNodes) {
                 node.animation.end();
@@ -241,6 +245,21 @@
     }
 
     /**
+     * Returns true if any of the child animations of this Sequencer have been started and have not
+     * yet ended.
+     * @return Whether this Sequencer has been started and has not yet ended.
+     */
+    @Override
+    public boolean isRunning() {
+        for (Node node : mNodes) {
+            if (node.animation.isRunning()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * {@inheritDoc}
      *
      * <p>Starting this <code>Sequencer</code> will, in turn, start the animations for which
@@ -467,14 +486,10 @@
         public void onAnimationEnd(Animatable animation) {
             animation.removeListener(this);
             mPlayingSet.remove(animation);
-            Node animNode = mSequencer.mNodeMap.get(animation);
-            animNode.done = true;
             ArrayList<Node> sortedNodes = mSequencer.mSortedNodes;
-            int numNodes = sortedNodes.size();
-            int nodeIndex = sortedNodes.indexOf(animNode);
             boolean allDone = true;
-            for (int i = nodeIndex + 1; i < numNodes; ++i) {
-                if (!sortedNodes.get(i).done) {
+            for (Node node : sortedNodes) {
+                if (node.animation.isRunning()) {
                     allDone = false;
                     break;
                 }
@@ -558,7 +573,6 @@
                         }
                     }
                 }
-                node.done = false; // also reset done flag
             }
         }
     }
@@ -626,13 +640,6 @@
         public ArrayList<Node> nodeDependents = null;
 
         /**
-         * Flag indicating whether the animation in this node is finished. This flag
-         * is used by Sequencer to check, as each animation ends, whether all child animations
-         * are done and it's time to send out an end event for the entire Sequencer.
-         */
-        public boolean done = false;
-
-        /**
          * Constructs the Node with the animation that it encapsulates. A Node has no
          * dependencies by default; dependencies are added via the addDependency()
          * method.
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a93184d..73e8d31 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1248,8 +1248,9 @@
         
         case IS_USER_A_MONKEY_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
-            reply.writeInt(isUserAMonkey() ? 1 : 0);
+            boolean areThey = isUserAMonkey();
             reply.writeNoException();
+            reply.writeInt(areThey ? 1 : 0);
             return true;
         }
         
@@ -1263,8 +1264,9 @@
         case IS_IMMERSIVE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
-            reply.writeInt(isImmersive(token) ? 1 : 0);
+            boolean isit = isImmersive(token);
             reply.writeNoException();
+            reply.writeInt(isit ? 1 : 0);
             return true;
         }
 
@@ -1279,8 +1281,9 @@
         
         case IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
-            reply.writeInt(isTopActivityImmersive() ? 1 : 0);
+            boolean isit = isTopActivityImmersive();
             reply.writeNoException();
+            reply.writeInt(isit ? 1 : 0);
             return true;
         }
 
@@ -1294,6 +1297,40 @@
             reply.writeNoException();
             return true;
         }
+        
+        case NEW_URI_PERMISSION_OWNER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String name = data.readString();
+            IBinder perm = newUriPermissionOwner(name);
+            reply.writeNoException();
+            reply.writeStrongBinder(perm);
+            return true;
+        }
+
+        case GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder owner = data.readStrongBinder();
+            int fromUid = data.readInt();
+            String targetPkg = data.readString();
+            Uri uri = Uri.CREATOR.createFromParcel(data);
+            int mode = data.readInt();
+            grantUriPermissionFromOwner(owner, fromUid, targetPkg, uri, mode);
+            reply.writeNoException();
+            return true;
+        }
+
+        case REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder owner = data.readStrongBinder();
+            Uri uri = null;
+            if (data.readInt() != 0) {
+                Uri.CREATOR.createFromParcel(data);
+            }
+            int mode = data.readInt();
+            revokeUriPermissionFromOwner(owner, uri, mode);
+            reply.writeNoException();
+            return true;
+        }
 
         case DUMP_HEAP_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
@@ -2854,8 +2891,8 @@
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
         mRemote.transact(IS_IMMERSIVE_TRANSACTION, data, reply, 0);
-        boolean res = reply.readInt() == 1;
         reply.readException();
+        boolean res = reply.readInt() == 1;
         data.recycle();
         reply.recycle();
         return res;
@@ -2867,8 +2904,8 @@
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         mRemote.transact(IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION, data, reply, 0);
-        boolean res = reply.readInt() == 1;
         reply.readException();
+        boolean res = reply.readInt() == 1;
         data.recycle();
         reply.recycle();
         return res;
@@ -2889,6 +2926,55 @@
         reply.recycle();
     }
 
+    public IBinder newUriPermissionOwner(String name)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(name);
+        mRemote.transact(NEW_URI_PERMISSION_OWNER_TRANSACTION, data, reply, 0);
+        reply.readException();
+        IBinder res = reply.readStrongBinder();
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
+    public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
+            Uri uri, int mode) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(owner);
+        data.writeInt(fromUid);
+        data.writeString(targetPkg);
+        uri.writeToParcel(data, 0);
+        data.writeInt(mode);
+        mRemote.transact(GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    public void revokeUriPermissionFromOwner(IBinder owner, Uri uri,
+            int mode) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(owner);
+        if (uri != null) {
+            data.writeInt(1);
+            uri.writeToParcel(data, 0);
+        } else {
+            data.writeInt(0);
+        }
+        data.writeInt(mode);
+        mRemote.transact(REVOKE_URI_PERMISSION_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     public boolean dumpHeap(String process, boolean managed,
             String path, ParcelFileDescriptor fd) throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -2910,6 +2996,6 @@
         data.recycle();
         return res;
     }
-
+    
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index e34bad2..fee95aa 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2878,6 +2878,11 @@
                 }
             }
 
+            public void startCommit() {
+                // TODO: implement
+                commit();
+            }
+
             public boolean commit() {
                 boolean returnValue;
 
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index f190d0d..c63233e 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.animation.Animatable;
+import android.animation.AnimatableInflater;
 import android.animation.PropertyAnimator;
 import android.animation.Sequencer;
 import android.content.res.TypedArray;
@@ -31,7 +32,6 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
 
 import java.util.ArrayList;
 
@@ -257,7 +257,7 @@
         }
         
         if (fragment.mNextAnim != 0) {
-            Animatable anim = AnimationUtils.loadAnimator(mActivity, fragment.mNextAnim);
+            Animatable anim = AnimatableInflater.loadAnimatable(mActivity, fragment.mNextAnim);
             if (anim != null) {
                 return anim;
             }
@@ -288,7 +288,7 @@
             return null;
         }
         
-        return AnimationUtils.loadAnimator(mActivity, anim);
+        return AnimatableInflater.loadAnimatable(mActivity, anim);
     }
     
     void moveToState(Fragment f, int newState, int transit, int transitionStyle) {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 5514582..901f117 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -317,6 +317,12 @@
     
     public void crashApplication(int uid, int initialPid, String packageName,
             String message) throws RemoteException;
+    
+    public IBinder newUriPermissionOwner(String name) throws RemoteException;
+    public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
+            Uri uri, int mode) throws RemoteException;
+    public void revokeUriPermissionFromOwner(IBinder owner, Uri uri,
+            int mode) throws RemoteException;
 
     // Cause the specified process to dump the specified heap.
     public boolean dumpHeap(String process, boolean managed, String path,
@@ -528,5 +534,8 @@
     int SET_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+111;
     int IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+112;
     int CRASH_APPLICATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+113;
-    int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+114;
+    int NEW_URI_PERMISSION_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+114;
+    int GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+115;
+    int REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+116;
+    int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+117;
 }
diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java
index e6dedc1..ce047f6 100644
--- a/core/java/android/content/ContentValues.java
+++ b/core/java/android/content/ContentValues.java
@@ -497,6 +497,10 @@
         return (ArrayList<String>) mValues.get(key);
     }
 
+    /**
+     * Returns a string containing a concise, human-readable description of this object.
+     * @return a printable representation of this object.
+     */
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index 5847216..6ee716a 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -163,14 +163,47 @@
          * {@link SharedPreferences} object it is editing.  This atomically
          * performs the requested modifications, replacing whatever is currently
          * in the SharedPreferences.
-         * 
+         *
          * <p>Note that when two editors are modifying preferences at the same
          * time, the last one to call commit wins.
-         * 
+         *
+         * <p>If you don't care about the return value and you're
+         * using this from your application's main thread, consider
+         * using {@link #startCommit} instead.
+         *
          * @return Returns true if the new values were successfully written
          * to persistent storage.
          */
         boolean commit();
+
+        /**
+         * Commit your preferences changes back from this Editor to the
+         * {@link SharedPreferences} object it is editing.  This atomically
+         * performs the requested modifications, replacing whatever is currently
+         * in the SharedPreferences.
+         *
+         * <p>Note that when two editors are modifying preferences at the same
+         * time, the last one to call commit wins.
+         *
+         * <p>Unlike {@link #commit}, which writes its preferences out
+         * to persistent storage synchronously, {@link #startCommit}
+         * commits its changes to the in-memory
+         * {@link SharedPreferences} immediately but starts an
+         * asynchronous commit to disk and you won't be notified of
+         * any failures.  If another editor on this
+         * {@link SharedPreferences} does a regular {@link #commit}
+         * while a {@link #startCommit} is still outstanding, the
+         * {@link #commit} will block until all async commits are
+         * completed as well as the commit itself.
+         *
+         * <p>If you call this from an {@link android.app.Activity},
+         * the base class will wait for any async commits to finish in
+         * its {@link android.app.Activity#onPause}.</p>
+         *
+         * @return Returns true if the new values were successfully written
+         * to persistent storage.
+         */
+        void startCommit();
     }
 
     /**
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 97921fe..0068724 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -34,11 +34,19 @@
 import java.util.List;
 
 /**
+ * <p>
  * SensorManager lets you access the device's {@link android.hardware.Sensor
  * sensors}. Get an instance of this class by calling
  * {@link android.content.Context#getSystemService(java.lang.String)
  * Context.getSystemService()} with the argument
  * {@link android.content.Context#SENSOR_SERVICE}.
+ * </p>
+ * <p>
+ * Always make sure to disable sensors you don't need, especially when your
+ * activity is paused. Failing to do so can drain the battery in just a few
+ * hours. Note that the system will <i>not</i> disable sensors automatically when
+ * the screen turns off.
+ * </p>
  *
  * <pre class="prettyprint">
  * public class SensorActivity extends Activity, implements SensorEventListener {
@@ -48,13 +56,22 @@
  *     public SensorActivity() {
  *         mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
  *         mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ *     }
+ *
+ *     protected void onResume() {
+ *         super.onResume();
  *         mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
  *     }
  *
+ *     protected void onPause() {
+ *         super.onPause();
+ *         mSensorManager.unregisterListener(this);
+ *     }
+ *
  *     public void onAccuracyChanged(Sensor sensor, int accuracy) {
  *     }
  *
- *     public abstract void onSensorChanged(SensorEvent event) {
+ *     public void onSensorChanged(SensorEvent event) {
  *     }
  * }
  * </pre>
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 227d94d..60ccdaf 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -322,6 +322,8 @@
         private static final int MICRO_KIND = 3;
         private static final String[] PROJECTION = new String[] {_ID, MediaColumns.DATA};
         static final int DEFAULT_GROUP_ID = 0;
+        private static final Object sThumbBufLock = new Object();
+        private static byte[] sThumbBuf;
 
         private static Bitmap getMiniThumbFromFile(Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options) {
             Bitmap bitmap = null;
@@ -397,11 +399,15 @@
                 long magic = thumbFile.getMagic(origId);
                 if (magic != 0) {
                     if (kind == MICRO_KIND) {
-                        byte[] data = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
-                        if (thumbFile.getMiniThumbFromFile(origId, data) != null) {
-                            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
-                            if (bitmap == null) {
-                                Log.w(TAG, "couldn't decode byte array.");
+                        synchronized (sThumbBufLock) {
+                            if (sThumbBuf == null) {
+                                sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
+                            }
+                            if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
+                                bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
+                                if (bitmap == null) {
+                                    Log.w(TAG, "couldn't decode byte array.");
+                                }
                             }
                         }
                         return bitmap;
@@ -427,11 +433,15 @@
 
                 // Assuming thumbnail has been generated, at least original image exists.
                 if (kind == MICRO_KIND) {
-                    byte[] data = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
-                    if (thumbFile.getMiniThumbFromFile(origId, data) != null) {
-                        bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
-                        if (bitmap == null) {
-                            Log.w(TAG, "couldn't decode byte array.");
+                    synchronized (sThumbBufLock) {
+                        if (sThumbBuf == null) {
+                            sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
+                        }
+                        if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
+                            bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
+                            if (bitmap == null) {
+                                Log.w(TAG, "couldn't decode byte array.");
+                            }
                         }
                     }
                 } else if (kind == MINI_KIND) {
diff --git a/core/java/android/provider/Mtp.java b/core/java/android/provider/Mtp.java
index bc764ac..26aa7d8 100644
--- a/core/java/android/provider/Mtp.java
+++ b/core/java/android/provider/Mtp.java
@@ -311,9 +311,12 @@
         // Object properties we support
         public static final int PROPERTY_STORAGE_ID = 0xDC01;
         public static final int PROPERTY_OBJECT_FORMAT = 0xDC02;
+        public static final int PROPERTY_PROTECTION_STATUS = 0xDC03;
         public static final int PROPERTY_OBJECT_SIZE = 0xDC04;
         public static final int PROPERTY_OBJECT_FILE_NAME = 0xDC07;
+        public static final int PROPERTY_DATE_MODIFIED = 0xDC09;
         public static final int PROPERTY_PARENT_OBJECT = 0xDC0B;
+        public static final int PROPERTY_PERSISTENT_UID = 0xDC41;
 
         /**
          * Object is not protected. It may be modified and deleted, and its properties
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 0e3522e..2f7482c 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -75,7 +75,7 @@
             }
         }
         tl = new TextLine();
-        Log.e("TLINE", "new: " + tl);
+        Log.v("TLINE", "new: " + tl);
         return tl;
     }
 
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 32d8123..42e3641 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -53,8 +53,10 @@
 
     /**
      * Destroys the hardware rendering context.
+     * 
+     * @param full If true, destroys all associated resources.
      */
-    abstract void destroy();
+    abstract void destroy(boolean full);
 
     /**
      * Initializes the hardware renderer for the specified surface.
@@ -187,7 +189,7 @@
         }
 
         /**
-         * Checks for OpenGL errors. If an error has occured, {@link #destroy()}
+         * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
          * is invoked and the requested flag is turned off. The error code is
          * also logged as a warning.
          */
@@ -197,7 +199,7 @@
                 if (error != EGL10.EGL_SUCCESS) {
                     // something bad has happened revert to
                     // normal rendering.
-                    destroy();
+                    destroy(true);
                     if (error != EGL11.EGL_CONTEXT_LOST) {
                         // we'll try again if it was context lost
                         setRequested(false);
@@ -217,13 +219,12 @@
                 if (mGl != null) {
                     int err = sEgl.eglGetError();
                     if (err != EGL10.EGL_SUCCESS) {
-                        destroy();
+                        destroy(true);
                         setRequested(false);
                     } else {
-                        if (mCanvas != null) {
-                            destroyCanvas();
+                        if (mCanvas == null) {
+                            mCanvas = createCanvas();
                         }
-                        mCanvas = createCanvas();
                         if (mCanvas != null) {
                             setEnabled(true);
                         } else {
@@ -237,11 +238,6 @@
             return false;
         }
 
-        private void destroyCanvas() {
-            mCanvas.destroy();
-            mCanvas = null;
-        }
-
         abstract GLES20Canvas createCanvas();
 
         void initializeEgl() {
@@ -341,16 +337,16 @@
         }
         
         @Override
-        void destroy() {
+        void destroy(boolean full) {
+            if (full && mCanvas != null) {
+                mCanvas.destroy();
+                mCanvas = null;
+            }
+            
             if (!isEnabled() || mDestroyed) return;
 
             mDestroyed = true;
 
-            checkCurrent();
-            // Destroy the Canvas first in case it needs to use a GL context
-            // to perform its cleanup.
-            destroyCanvas();
-
             sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
                     EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
             sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
@@ -358,12 +354,6 @@
             mEglSurface = null;
             mGl = null;
 
-            // mEgl.eglDestroyContext(mEglDisplay, mEglContext);
-            // mEglContext = null;            
-            // mEgl.eglTerminate(mEglDisplay);
-            // mEgl = null;
-            // mEglDisplay = null;
-
             setEnabled(false);
         }
 
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 479e757..458274f 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -349,6 +349,7 @@
     public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
         synchronized (mConstructorArgs) {
             final AttributeSet attrs = Xml.asAttributeSet(parser);
+            Context lastContext = (Context)mConstructorArgs[0];
             mConstructorArgs[0] = mContext;
             View result = root;
 
@@ -433,6 +434,10 @@
                         + ": " + e.getMessage());
                 ex.initCause(e);
                 throw ex;
+            } finally {
+                // Don't retain static reference on context.
+                mConstructorArgs[0] = lastContext;
+                mConstructorArgs[1] = null;
             }
 
             return result;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b565fc6..616128d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -262,7 +262,8 @@
  * <ul>
  * <li>Define a Button in the layout file and assign it a unique ID.
  * <pre>
- * &lt;Button id="@+id/my_button"
+ * &lt;Button
+ *     android:id="@+id/my_button"
  *     android:layout_width="wrap_content"
  *     android:layout_height="wrap_content"
  *     android:text="@string/my_button_text"/&gt;
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 402443c..7b6991f 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1138,22 +1138,24 @@
 
         final View captureView = findView(root, parameter);
         Bitmap b = performViewCapture(captureView, false);
-       
-        if (b != null) {
-            BufferedOutputStream out = null;
-            try {
-                out = new BufferedOutputStream(clientStream, 32 * 1024);
-                b.compress(Bitmap.CompressFormat.PNG, 100, out);
-                out.flush();
-            } finally {
-                if (out != null) {
-                    out.close();
-                }
-                b.recycle();
-            }
-        } else {
+
+        if (b == null) {
             Log.w("View", "Failed to create capture bitmap!");
-            clientStream.close();
+            // Send an empty one so that it doesn't get stuck waiting for
+            // something.
+            b = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+        }
+
+        BufferedOutputStream out = null;
+        try {
+            out = new BufferedOutputStream(clientStream, 32 * 1024);
+            b.compress(Bitmap.CompressFormat.PNG, 100, out);
+            out.flush();
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+            b.recycle();
         }
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 3678684..9770313 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -970,7 +970,12 @@
             mMotionTarget = null;
         }
 
-        return target.dispatchTouchEvent(ev);
+        if (target.dispatchTouchEvent(ev)) {
+            return true;
+        } else {
+            ev.setLocation(xf, yf);
+        }
+        return false;
     }
 
     /**
@@ -1006,7 +1011,12 @@
             // There are many subtle interactions in touch event dispatch; change at your own risk.
             child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
             ev.offsetLocation(localX - x, localY - y);
-            return child.dispatchTouchEvent(ev);
+            if (child.dispatchTouchEvent(ev)) {
+                return true;
+            } else {
+                ev.offsetLocation(x - localX, y - localY);
+                return false;
+            }
         }
         return false;
     }
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 5999aba..a91d2bd 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -469,7 +469,9 @@
             if (attrs != null &&
                     (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) {
                 final boolean translucent = attrs.format != PixelFormat.OPAQUE;
-                destroyHardwareRenderer();
+                if (mHwRenderer != null) {
+                    mHwRenderer.destroy(true);
+                }                
                 mHwRenderer = HardwareRenderer.createGlRenderer(2, translucent);
             }
         }
@@ -678,7 +680,9 @@
             attachInfo.mWindowVisibility = viewVisibility;
             host.dispatchWindowVisibilityChanged(viewVisibility);
             if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
-                destroyHardwareRenderer();
+                if (mHwRenderer != null) {
+                    mHwRenderer.destroy(false);
+                }                
             }
             if (viewVisibility == View.GONE) {
                 // After making a window gone, we will count it as being
@@ -1597,9 +1601,11 @@
         mAttachInfo.mRootView = null;
         mAttachInfo.mSurface = null;
 
-        destroyHardwareRenderer();
-        mHwRenderer = null;
-        
+        if (mHwRenderer != null) {
+            mHwRenderer.destroy(true);
+            mHwRenderer = null;
+        }
+
         mSurface.release();
 
         if (mInputChannel != null) {
@@ -1624,12 +1630,6 @@
         }
     }
 
-    private void destroyHardwareRenderer() {
-        if (mHwRenderer != null) {
-            mHwRenderer.destroy();
-        }
-    }
-
     void updateConfiguration(Configuration config, boolean force) {
         if (DEBUG_CONFIGURATION) Log.v(TAG,
                 "Applying new config to window "
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 8e4cf67..c090f8e 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -16,12 +16,6 @@
 
 package android.view.animation;
 
-import android.animation.Animatable;
-import android.animation.Animator;
-import android.animation.PropertyAnimator;
-import android.animation.Sequencer;
-import android.content.res.TypedArray;
-import android.util.TypedValue;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -33,7 +27,6 @@
 import android.os.SystemClock;
 
 import java.io.IOException;
-import java.util.ArrayList;
 
 /**
  * Defines common utilities for working with animations.
@@ -92,42 +85,6 @@
         }
     }
 
-    /**
-     * Loads an {@link Animation} object from a resource
-     *
-     * @param context Application context used to access resources
-     * @param id The resource id of the animation to load
-     * @return The animation object reference by the specified id
-     * @throws NotFoundException when the animation cannot be loaded
-     */
-    public static Animatable loadAnimator(Context context, int id)
-            throws NotFoundException {
-
-        XmlResourceParser parser = null;
-        try {
-            parser = context.getResources().getAnimation(id);
-            return createAnimatableFromXml(context, parser);
-        } catch (XmlPullParserException ex) {
-            NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
-                    Integer.toHexString(id));
-            rnf.initCause(ex);
-            throw rnf;
-        } catch (IOException ex) {
-            NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
-                    Integer.toHexString(id));
-            rnf.initCause(ex);
-            throw rnf;
-        } finally {
-            if (parser != null) parser.close();
-        }
-    }
-
-    private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser)
-            throws XmlPullParserException, IOException {
-
-        return createAnimatableFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
-    }
-
     private static Animation createAnimationFromXml(Context c, XmlPullParser parser)
             throws XmlPullParserException, IOException {
 
@@ -176,66 +133,6 @@
 
     }
 
-    private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser,
-            AttributeSet attrs, Sequencer parent, int sequenceOrdering)
-            throws XmlPullParserException, IOException {
-
-        Animatable anim = null;
-        ArrayList<Animatable> childAnims = null;
-
-        // Make sure we are on a start tag.
-        int type;
-        int depth = parser.getDepth();
-
-        while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
-               && type != XmlPullParser.END_DOCUMENT) {
-
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            String  name = parser.getName();
-
-            if (name.equals("property")) {
-                anim = new PropertyAnimator(c, attrs);
-            } else if (name.equals("animator")) {
-                anim = new Animator(c, attrs);
-            } else if (name.equals("sequencer")) {
-                anim = new Sequencer();
-                TypedArray a = c.obtainStyledAttributes(attrs,
-                        com.android.internal.R.styleable.Sequencer);
-                int ordering = a.getInt(com.android.internal.R.styleable.Sequencer_ordering,
-                        TOGETHER);
-                createAnimatableFromXml(c, parser, attrs, (Sequencer) anim,  ordering);
-                a.recycle();
-            } else {
-                throw new RuntimeException("Unknown animator name: " + parser.getName());
-            }
-
-            if (parent != null) {
-                if (childAnims == null) {
-                    childAnims = new ArrayList<Animatable>();
-                }
-                childAnims.add(anim);
-            }
-        }
-        if (parent != null && childAnims != null) {
-            Animatable[] animsArray = new Animatable[childAnims.size()];
-            int index = 0;
-            for (Animatable a : childAnims) {
-                animsArray[index++] = a;
-            }
-            if (sequenceOrdering == TOGETHER) {
-                parent.playTogether(animsArray);
-            } else {
-                parent.playSequentially(animsArray);
-            }
-        }
-
-        return anim;
-
-    }
-
     public static LayoutAnimationController loadLayoutAnimation(Context context, int id)
             throws NotFoundException {
         
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5d9ab90..682319f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1376,6 +1376,7 @@
                                SslCertificate.saveState(mCertificate));
         }
         outState.putBoolean("privateBrowsingEnabled", isPrivateBrowsingEnabled());
+        mZoomManager.saveZoomState(outState);
         return list;
     }
 
@@ -1542,6 +1543,7 @@
             if (inState.getBoolean("privateBrowsingEnabled")) {
                 getSettings().setPrivateBrowsingEnabled(true);
             }
+            mZoomManager.restoreZoomState(inState);
             // Remove all pending messages because we are restoring previous
             // state.
             mWebViewCore.removeMessages();
@@ -3355,7 +3357,8 @@
         setUpSelect();
         if (mNativeClass != 0 && nativeWordSelection(x, y)) {
             nativeSetExtendSelection();
-            getWebChromeClient().onSelectionStart(this);
+            WebChromeClient client = getWebChromeClient();
+            if (client != null) client.onSelectionStart(this);
             return true;
         }
         notifySelectDialogDismissed();
@@ -4088,7 +4091,7 @@
                 return true;
             }
             clearTextEntry();
-            nativeSetFollowedLink(true);
+            nativeShowCursorTimed();
             if (!mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
                 mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
                         nativeCursorNodePointer());
@@ -4175,7 +4178,8 @@
      */
     public void selectionDone() {
         if (mSelectingText) {
-            getWebChromeClient().onSelectionDone(this);
+            WebChromeClient client = getWebChromeClient();
+            if (client != null) client.onSelectionDone(this);
             invalidate(); // redraw without selection
             notifySelectDialogDismissed();
         }
@@ -7327,9 +7331,9 @@
     private native void     nativeSetExtendSelection();
     private native void     nativeSetFindIsEmpty();
     private native void     nativeSetFindIsUp(boolean isUp);
-    private native void     nativeSetFollowedLink(boolean followed);
     private native void     nativeSetHeightCanMeasure(boolean measure);
     private native void     nativeSetBaseLayer(int layer);
+    private native void     nativeShowCursorTimed();
     private native void     nativeReplaceBaseContent(int content);
     private native void     nativeCopyBaseContentToPicture(Picture pict);
     private native boolean  nativeHasContent();
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index b6c8e47..e78c8b7 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Parcel;
@@ -323,15 +324,13 @@
         return null;
     }
 
-    private LayoutParams createOrReuseLayoutParams(View v) {
+    LayoutParams createOrReuseLayoutParams(View v) {
         final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
-        if (currentLp instanceof LayoutParams) {
+        if (currentLp instanceof ViewGroup.LayoutParams) {
             LayoutParams lp = (LayoutParams) currentLp;
-            lp.setHorizontalOffset(0);
-            lp.setVerticalOffset(0);
             return lp;
         }
-        return new LayoutParams(v);
+        return new ViewGroup.LayoutParams(0, 0);
     }
 
     void showOnly(int childIndex, boolean animate, boolean onLayout) {
@@ -472,10 +471,9 @@
 
             int childRight = mPaddingLeft + child.getMeasuredWidth();
             int childBottom = mPaddingTop + child.getMeasuredHeight();
-            LayoutParams lp = (LayoutParams) child.getLayoutParams();
 
-            child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset,
-                    childRight + lp.horizontalOffset, childBottom + lp.verticalOffset);
+            child.layout(mPaddingLeft, mPaddingTop,
+                    childRight, childBottom);
         }
         mDataChanged = false;
     }
@@ -738,102 +736,4 @@
             setAdapter(mRemoteViewsAdapter);
         }
     }
-
-    private final Rect dirtyRect = new Rect();
-    @Override
-    public void removeViewInLayout(View view) {
-        // TODO: need to investigate this block a bit more
-        // and perhaps fix some other invalidations issues.
-        View parent = null;
-        view.setVisibility(INVISIBLE);
-        if (view.getLayoutParams() instanceof LayoutParams) {
-            LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            parent = lp.getParentAndDirtyRegion(dirtyRect);
-        }
-
-        super.removeViewInLayout(view);
-
-        if (parent != null)
-            parent.invalidate(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
-    }
-
-    static class LayoutParams extends ViewGroup.LayoutParams {
-        int horizontalOffset;
-        int verticalOffset;
-        View mView;
-
-        LayoutParams(View view) {
-            super(0, 0);
-            horizontalOffset = 0;
-            verticalOffset = 0;
-            mView = view;
-        }
-
-        LayoutParams(Context c, AttributeSet attrs) {
-            super(c, attrs);
-            horizontalOffset = 0;
-            verticalOffset = 0;
-        }
-
-        private Rect parentRect = new Rect();
-        void invalidateGlobalRegion(View v, Rect r) {
-            View p = v;
-            boolean firstPass = true;
-            parentRect.set(0, 0, 0, 0);
-            while (p.getParent() != null && p.getParent() instanceof View
-                    && !parentRect.contains(r)) {
-                if (!firstPass) r.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY());
-                firstPass = false;
-                p = (View) p.getParent();
-                parentRect.set(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY(),
-                        p.getRight() - p.getScrollX(), p.getBottom() - p.getScrollY());
-            }
-            p.invalidate(r.left, r.top, r.right, r.bottom);
-        }
-
-        public View getParentAndDirtyRegion(Rect globalRect) {
-            globalRect.set(mView.getLeft(), mView.getTop(), mView.getRight(), mView.getBottom());
-            View p = mView;
-            boolean firstPass = true;
-            parentRect.set(0, 0, 0, 0);
-            while (p.getParent() != null && p.getParent() instanceof View
-                    && !parentRect.contains(globalRect)) {
-                if (!firstPass) {
-                    globalRect.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY());
-                }
-
-                firstPass = false;
-                p = (View) p.getParent();
-                parentRect.set(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY(),
-                        p.getRight() - p.getScrollX(), p.getBottom() - p.getScrollY());
-            }
-            return p;
-        }
-
-        private Rect invalidateRect = new Rect();
-        // This is public so that PropertyAnimator can access it
-        public void setVerticalOffset(int newVerticalOffset) {
-            int offsetDelta = newVerticalOffset - verticalOffset;
-            verticalOffset = newVerticalOffset;
-            if (mView != null) {
-                mView.requestLayout();
-                int top = Math.min(mView.getTop() + offsetDelta, mView.getTop());
-                int bottom = Math.max(mView.getBottom() + offsetDelta, mView.getBottom());
-                invalidateRect.set(mView.getLeft(), top, mView.getRight(), bottom);
-                invalidateGlobalRegion(mView, invalidateRect);
-            }
-        }
-
-        public void setHorizontalOffset(int newHorizontalOffset) {
-            int offsetDelta = newHorizontalOffset - horizontalOffset;
-            horizontalOffset = newHorizontalOffset;
-            if (mView != null) {
-                mView.requestLayout();
-                int left = Math.min(mView.getLeft() + offsetDelta, mView.getLeft());
-                int right = Math.max(mView.getRight() + offsetDelta, mView.getRight());
-                invalidateRect.set(left, mView.getTop(), right, mView.getBottom());
-                invalidateGlobalRegion(mView, invalidateRect);
-            }
-        }
-    }
 }
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index c3e8838..b460adb 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -27,6 +27,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -66,8 +67,6 @@
     private static final float SLIDE_UP_RATIO = 0.7f;
 
     private final WeakHashMap<View, Float> mRotations = new WeakHashMap<View, Float>();
-    private final WeakHashMap<View, Integer>
-            mChildrenToApplyTransformsTo = new WeakHashMap<View, Integer>();
 
     /**
      * Sentinel value for no current active pointer.
@@ -90,9 +89,12 @@
     private int mMaximumVelocity;
     private VelocityTracker mVelocityTracker;
 
+    private static HolographicHelper sHolographicHelper;
     private ImageView mHighlight;
     private StackSlider mStackSlider;
     private boolean mFirstLayoutHappened = false;
+    private ViewGroup mAncestorContainingAllChildren = null;
+    private int mAncestorHeight = 0;
 
     public StackView(Context context) {
         super(context);
@@ -117,9 +119,11 @@
         addViewInLayout(mHighlight, -1, new LayoutParams(mHighlight));
         mStackSlider = new StackSlider();
 
-        if (!sPaintsInitialized) {
-            initializePaints();
+        if (sHolographicHelper == null) {
+            sHolographicHelper = new HolographicHelper();
         }
+        setClipChildren(false);
+        setClipToPadding(false);
     }
 
     /**
@@ -205,6 +209,7 @@
         if (!mRotations.containsKey(child)) {
             float rotation = (float) (Math.random()*26 - 13);
             mRotations.put(child, rotation);
+            child.setRotation(rotation);
         }
 
         // Child has been removed
@@ -212,47 +217,36 @@
             if (mRotations.containsKey(child)) {
                 mRotations.remove(child);
             }
-            if (mChildrenToApplyTransformsTo.containsKey(child)) {
-                mChildrenToApplyTransformsTo.remove(child);
-            }
         }
-
-        // if this view is already in the layout, we need to
-        // wait until layout has finished in order to set the
-        // pivot point of the rotation (requiring getMeasuredWidth/Height())
-        mChildrenToApplyTransformsTo.put(child, relativeIndex);
     }
 
     @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+    }
 
-        if (!mChildrenToApplyTransformsTo.isEmpty()) {
-            for (View child: mChildrenToApplyTransformsTo.keySet()) {
-                if (mRotations.containsKey(child)) {
-                    child.setPivotX(child.getMeasuredWidth()/2);
-                    child.setPivotY(child.getMeasuredHeight()/2);
-                    child.setRotation(mRotations.get(child));
-                }
+    // TODO: right now, this code walks up the hierarchy as far as needed and disables clipping
+    // so that the stack's children can draw outside of the stack's bounds. This is fine within
+    // the context of widgets in the launcher, but is destructive in general, as the clipping
+    // values are not being reset. For this to be a full framework level widget, we will need
+    // framework level support for drawing outside of a parent's bounds.
+    private void disableParentalClipping() {
+        if (mAncestorContainingAllChildren != null) {
+            Log.v(TAG, "Disabling parental clipping.");
+            ViewGroup vg = this;
+            while (vg.getParent() != null && vg.getParent() instanceof ViewGroup) {
+                if (vg == mAncestorContainingAllChildren) break;
+                vg = (ViewGroup) vg.getParent();
+                vg.setClipChildren(false);
+                vg.setClipToPadding(false);
             }
-            mChildrenToApplyTransformsTo.clear();
         }
+    }
 
+    private void onLayout() {
         if (!mFirstLayoutHappened) {
             mViewHeight = Math.round(SLIDE_UP_RATIO*getMeasuredHeight());
             mSwipeThreshold = Math.round(SWIPE_THRESHOLD_RATIO*mViewHeight);
-
-            // TODO: Right now this walks all the way up the view hierarchy and disables
-            // ClipChildren and ClipToPadding. We're probably going  to want to reset
-            // these flags as well.
-            setClipChildren(false);
-            setClipToPadding(false);
-            ViewGroup view = this;
-            while (view.getParent() != null && view.getParent() instanceof ViewGroup) {
-                view = (ViewGroup) view.getParent();
-                view.setClipChildren(false);
-                view.setClipToPadding(false);
-            }
             mFirstLayoutHappened = true;
         }
     }
@@ -261,7 +255,6 @@
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         int action = ev.getAction();
         switch(action & MotionEvent.ACTION_MASK) {
-
             case MotionEvent.ACTION_DOWN: {
                 if (mActivePointerId == INVALID_POINTER) {
                     mInitialX = ev.getX();
@@ -320,7 +313,7 @@
             View v = getViewAtRelativeIndex(activeIndex);
             if (v == null) return;
 
-            mHighlight.setImageBitmap(createOutline(v));
+            mHighlight.setImageBitmap(sHolographicHelper.createOutline(v));
             mHighlight.bringToFront();
             v.bringToFront();
             mStackSlider.setView(v);
@@ -640,7 +633,6 @@
         float getXProgress() {
             return mXProgress;
         }
-
     }
 
     @Override
@@ -649,55 +641,206 @@
         setDisplayedChild(mWhichChild);
     }
 
-    private static final Paint sHolographicPaint = new Paint();
-    private static final Paint sErasePaint = new Paint();
-    private static boolean sPaintsInitialized = false;
-    private static final float STROKE_WIDTH = 3.0f;
-
-    static void initializePaints() {
-        sHolographicPaint.setColor(0xff6699ff);
-        sHolographicPaint.setFilterBitmap(true);
-        sErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
-        sErasePaint.setFilterBitmap(true);
-        sPaintsInitialized = true;
+    LayoutParams createOrReuseLayoutParams(View v) {
+        final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
+        if (currentLp instanceof LayoutParams) {
+            LayoutParams lp = (LayoutParams) currentLp;
+            lp.setHorizontalOffset(0);
+            lp.setVerticalOffset(0);
+            return lp;
+        }
+        return new LayoutParams(v);
     }
 
-    static Bitmap createOutline(View v) {
-        if (v.getMeasuredWidth() == 0 || v.getMeasuredHeight() == 0) {
-            return null;
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        boolean dataChanged = mDataChanged;
+        if (dataChanged) {
+            handleDataChanged();
+
+            // if the data changes, mWhichChild might be out of the bounds of the adapter
+            // in this case, we reset mWhichChild to the beginning
+            if (mWhichChild >= mAdapter.getCount())
+                mWhichChild = 0;
+
+            showOnly(mWhichChild, true, true);
         }
 
-        Bitmap bitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(),
-                Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
 
-        float rotationX = v.getRotationX();
-        v.setRotationX(0);
-        canvas.concat(v.getMatrix());
-        v.draw(canvas);
-        v.setRotationX(rotationX);
+            int childRight = mPaddingLeft + child.getMeasuredWidth();
+            int childBottom = mPaddingTop + child.getMeasuredHeight();
+            LayoutParams lp = (LayoutParams) child.getLayoutParams();
 
-        Bitmap outlineBitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(),
-                Bitmap.Config.ARGB_8888);
-        Canvas outlineCanvas = new Canvas(outlineBitmap);
-        drawOutline(outlineCanvas, bitmap);
-        bitmap.recycle();
-        return outlineBitmap;
+            child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset,
+                    childRight + lp.horizontalOffset, childBottom + lp.verticalOffset);
+
+            //TODO: temp until fix in View
+            child.setPivotX(child.getMeasuredWidth()/2);
+            child.setPivotY(child.getMeasuredHeight()/2);
+        }
+
+        mDataChanged = false;
+        onLayout();
     }
 
-    static void drawOutline(Canvas dest, Bitmap src) {
-        dest.drawColor(0, PorterDuff.Mode.CLEAR);
+    class LayoutParams extends ViewGroup.LayoutParams {
+        int horizontalOffset;
+        int verticalOffset;
+        View mView;
 
-        Bitmap mask = src.extractAlpha();
-        Matrix id = new Matrix();
+        LayoutParams(View view) {
+            super(0, 0);
+            horizontalOffset = 0;
+            verticalOffset = 0;
+            mView = view;
+        }
 
-        Matrix m = new Matrix();
-        float xScale = STROKE_WIDTH*2/(src.getWidth());
-        float yScale = STROKE_WIDTH*2/(src.getHeight());
-        m.preScale(1+xScale, 1+yScale, src.getWidth()/2, src.getHeight()/2);
-        dest.drawBitmap(mask, m, sHolographicPaint);
+        LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+            horizontalOffset = 0;
+            verticalOffset = 0;
+        }
 
-        dest.drawBitmap(src, id, sErasePaint);
-        mask.recycle();
+        private Rect parentRect = new Rect();
+        void invalidateGlobalRegion(View v, Rect r) {
+            View p = v;
+            if (!(v.getParent() != null && v.getParent() instanceof View)) return;
+
+            View gp = (View) v.getParent();
+            boolean firstPass = true;
+            parentRect.set(0, 0, 0, 0);
+            int depth = 0;
+            while (gp.getParent() != null && gp.getParent() instanceof View
+                    && !parentRect.contains(r)) {
+                if (!firstPass) {
+                    r.offset(p.getLeft() - gp.getScrollX(), p.getTop() - gp.getScrollY());
+                    depth++;
+                }
+                firstPass = false;
+                p = (View) p.getParent();
+                gp = (View) p.getParent();
+                parentRect.set(p.getLeft() - gp.getScrollX(), p.getTop() - gp.getScrollY(),
+                        p.getRight() - gp.getScrollX(), p.getBottom() - gp.getScrollY());
+            }
+
+            if (depth > mAncestorHeight) {
+                mAncestorContainingAllChildren = (ViewGroup) p;
+                mAncestorHeight = depth;
+                disableParentalClipping();
+            }
+
+            p.invalidate(r.left, r.top, r.right, r.bottom);
+        }
+
+        private Rect invalidateRect = new Rect();
+        private RectF invalidateRectf = new RectF();
+        // This is public so that PropertyAnimator can access it
+        public void setVerticalOffset(int newVerticalOffset) {
+            int offsetDelta = newVerticalOffset - verticalOffset;
+            verticalOffset = newVerticalOffset;
+
+            if (mView != null) {
+                mView.requestLayout();
+                int top = Math.min(mView.getTop() + offsetDelta, mView.getTop());
+                int bottom = Math.max(mView.getBottom() + offsetDelta, mView.getBottom());
+
+                invalidateRectf.set(mView.getLeft(),  top, mView.getRight(), bottom);
+
+                float xoffset = -invalidateRectf.left;
+                float yoffset = -invalidateRectf.top;
+                invalidateRectf.offset(xoffset, yoffset);
+                mView.getMatrix().mapRect(invalidateRectf);
+                invalidateRectf.offset(-xoffset, -yoffset);
+                invalidateRect.set((int) Math.floor(invalidateRectf.left),
+                        (int) Math.floor(invalidateRectf.top),
+                        (int) Math.ceil(invalidateRectf.right),
+                        (int) Math.ceil(invalidateRectf.bottom));
+
+                invalidateGlobalRegion(mView, invalidateRect);
+            }
+        }
+
+        public void setHorizontalOffset(int newHorizontalOffset) {
+            int offsetDelta = newHorizontalOffset - horizontalOffset;
+            horizontalOffset = newHorizontalOffset;
+
+            if (mView != null) {
+                mView.requestLayout();
+                int left = Math.min(mView.getLeft() + offsetDelta, mView.getLeft());
+                int right = Math.max(mView.getRight() + offsetDelta, mView.getRight());
+                invalidateRectf.set(left,  mView.getTop(), right, mView.getBottom());
+
+                float xoffset = -invalidateRectf.left;
+                float yoffset = -invalidateRectf.top;
+                invalidateRectf.offset(xoffset, yoffset);
+                mView.getMatrix().mapRect(invalidateRectf);
+                invalidateRectf.offset(-xoffset, -yoffset);
+
+                invalidateRect.set((int) Math.floor(invalidateRectf.left),
+                        (int) Math.floor(invalidateRectf.top),
+                        (int) Math.ceil(invalidateRectf.right),
+                        (int) Math.ceil(invalidateRectf.bottom));
+
+                invalidateGlobalRegion(mView, invalidateRect);
+            }
+        }
+    }
+
+    private static class HolographicHelper {
+        private final Paint mHolographicPaint = new Paint();
+        private final Paint mErasePaint = new Paint();
+        private final float STROKE_WIDTH = 3.0f;
+
+        HolographicHelper() {
+            initializePaints();
+        }
+
+        void initializePaints() {
+            mHolographicPaint.setColor(0xff6699ff);
+            mHolographicPaint.setFilterBitmap(true);
+            mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
+            mErasePaint.setFilterBitmap(true);
+        }
+
+        Bitmap createOutline(View v) {
+            if (v.getMeasuredWidth() == 0 || v.getMeasuredHeight() == 0) {
+                return null;
+            }
+
+            Bitmap bitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(),
+                    Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(bitmap);
+
+            float rotationX = v.getRotationX();
+            v.setRotationX(0);
+            canvas.concat(v.getMatrix());
+            v.draw(canvas);
+
+            v.setRotationX(rotationX);
+
+            drawOutline(canvas, bitmap);
+            return bitmap;
+        }
+
+        final Matrix id = new Matrix();
+        final Matrix scaleMatrix = new Matrix();
+        void drawOutline(Canvas dest, Bitmap src) {
+            Bitmap mask = src.extractAlpha();
+
+            dest.drawColor(0, PorterDuff.Mode.CLEAR);
+
+            float xScale = STROKE_WIDTH*2/(dest.getWidth());
+            float yScale = STROKE_WIDTH*2/(dest.getHeight());
+
+            scaleMatrix.reset();
+            scaleMatrix.preScale(1+xScale, 1+yScale, dest.getWidth()/2, dest.getHeight()/2);
+            dest.setMatrix(id);
+            dest.drawBitmap(mask, scaleMatrix, mHolographicPaint);
+            dest.drawBitmap(mask, id, mErasePaint);
+            mask.recycle();
+        }
     }
 }
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index ef0ab41..1048352 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -110,13 +110,13 @@
                 com.android.internal.R.id.lower_action_context_bar);
         mAnimatorView = (ViewAnimator) decor.findViewById(
                 com.android.internal.R.id.action_bar_animator);
-        mActionView.setContextView(mUpperContextView);
-        
+
         if (mActionView == null || mUpperContextView == null || mAnimatorView == null) {
             throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
                     "with a compatible window decor layout");
         }
 
+        mActionView.setContextView(mUpperContextView);
         mContextDisplayMode = mLowerContextView == null ?
                 CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT;
     }
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index dba1cea..6f6d919 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -506,7 +506,7 @@
 static void readLocale(char* language, char* region)
 {
     char propLang[PROPERTY_VALUE_MAX], propRegn[PROPERTY_VALUE_MAX];
-    
+
     property_get("persist.sys.language", propLang, "");
     property_get("persist.sys.country", propRegn, "");
     if (*propLang == 0 && *propRegn == 0) {
@@ -701,6 +701,42 @@
         }
     }
 
+    /* enable poisoning of memory of freed objects */
+    property_get("dalvik.vm.gc.overwritefree", propBuf, "false");
+    if (strcmp(propBuf, "true") == 0) {
+        opt.optionString = "-Xgc:overwritefree";
+        mOptions.add(opt);
+    } else if (strcmp(propBuf, "false") != 0) {
+        LOGW("dalvik.vm.gc.overwritefree should be 'true' or 'false'");
+    }
+
+    /* enable heap verification before each gc */
+    property_get("dalvik.vm.gc.preverify", propBuf, "false");
+    if (strcmp(propBuf, "true") == 0) {
+        opt.optionString = "-Xgc:preverify";
+        mOptions.add(opt);
+    } else if (strcmp(propBuf, "false") != 0) {
+        LOGW("dalvik.vm.gc.preverify should be 'true' or 'false'");
+    }
+
+    /* enable heap verification after each gc */
+    property_get("dalvik.vm.gc.postverify", propBuf, "false");
+    if (strcmp(propBuf, "true") == 0) {
+        opt.optionString = "-Xgc:postverify";
+        mOptions.add(opt);
+    } else if (strcmp(propBuf, "false") != 0) {
+        LOGW("dalvik.vm.gc.postverify should be 'true' or 'false'");
+    }
+
+    /* enable card table verification for partial gc */
+    property_get("dalvik.vm.gc.verifycardtable", propBuf, "false");
+    if (strcmp(propBuf, "true") == 0) {
+        opt.optionString = "-Xgc:verifycardtable";
+        mOptions.add(opt);
+    } else if (strcmp(propBuf, "false") != 0) {
+        LOGW("dalvik.vm.gc.verifycardtable should be 'true' or 'false'");
+    }
+
     /* enable debugging; set suspend=y to pause during VM init */
 #ifdef HAVE_ANDROID_OS
     /* use android ADB transport */
@@ -748,16 +784,6 @@
     }
 
 #if defined(WITH_JIT)
-    /* Minimal profile threshold to trigger JIT compilation */
-    char jitThresholdBuf[sizeof("-Xjitthreshold:") + PROPERTY_VALUE_MAX];
-    property_get("dalvik.vm.jit.threshold", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        strcpy(jitThresholdBuf, "-Xjitthreshold:");
-        strcat(jitThresholdBuf, propBuf);
-        opt.optionString = jitThresholdBuf;
-        mOptions.add(opt);
-    }
-
     /* Force interpreter-only mode for selected opcodes. Eg "1-0a,3c,f1-ff" */
     char jitOpBuf[sizeof("-Xjitop:") + PROPERTY_VALUE_MAX];
     property_get("dalvik.vm.jit.op", propBuf, "");
@@ -768,16 +794,6 @@
         mOptions.add(opt);
     }
 
-    /*
-     * Reverse the polarity of dalvik.vm.jit.op and force interpreter-only
-     * for non-selected opcodes.
-     */
-    property_get("dalvik.vm.jit.includeop", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        opt.optionString = "-Xincludeselectedop";
-        mOptions.add(opt);
-    }
-
     /* Force interpreter-only mode for selected methods */
     char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX];
     property_get("dalvik.vm.jit.method", propBuf, "");
@@ -787,37 +803,6 @@
         opt.optionString = jitMethodBuf;
         mOptions.add(opt);
     }
-
-    /*
-     * Reverse the polarity of dalvik.vm.jit.method and force interpreter-only
-     * for non-selected methods.
-     */
-    property_get("dalvik.vm.jit.includemethod", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        opt.optionString = "-Xincludeselectedmethod";
-        mOptions.add(opt);
-    }
-
-    /*
-     * Enable profile collection on JIT'ed code.
-     */
-    property_get("dalvik.vm.jit.profile", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        opt.optionString = "-Xjitprofile";
-        mOptions.add(opt);
-    }
-
-    /*
-     * Disable optimizations by setting the corresponding bit to 1.
-     */
-    char jitOptBuf[sizeof("-Xjitdisableopt:") + PROPERTY_VALUE_MAX];
-    property_get("dalvik.vm.jit.disableopt", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        strcpy(jitOptBuf, "-Xjitdisableopt:");
-        strcat(jitOptBuf, propBuf);
-        opt.optionString = jitOptBuf;
-        mOptions.add(opt);
-    }
 #endif
 
     if (executionMode == kEMIntPortable) {
diff --git a/core/res/res/raw-rm/loaderror.html b/core/res/res/raw-rm/loaderror.html
new file mode 100644
index 0000000..8e4a3fe
--- /dev/null
+++ b/core/res/res/raw-rm/loaderror.html
@@ -0,0 +1,18 @@
+<html>
+    <head>
+        <title>Pagina d'internet betg disponibla</title>
+        <style type="text/css">
+            body { margin-top: 0px; padding-top: 0px; }
+            h2   { margin-top: 5px; padding-top: 0px; }
+        </style>
+
+        <body>
+
+            <img src="file:///android_asset/webkit/android-weberror.png" align="top" />
+            <h2>Pagina d'internet betg disponibla</h2>
+            <p>Impussibel da chargiar la pagina d'internet sut <a href="%s">%s</a>. Errur:</p>
+            <!-- The %e is replaced by a localized error string -->
+            <p>%e</p>
+        </body>
+    </head>
+</html>
diff --git a/core/res/res/raw-rm/nodomain.html b/core/res/res/raw-rm/nodomain.html
new file mode 100644
index 0000000..1e2833b
--- /dev/null
+++ b/core/res/res/raw-rm/nodomain.html
@@ -0,0 +1,23 @@
+<html>
+    <head>
+        <title>Pagina d'internet betg disponibla</title>
+        <style type="text/css">
+            body { margin-top: 0px; padding-top: 0px; }
+            h2   { margin-top: 5px; padding-top: 0px; }
+        </style>
+
+        <body>
+
+            <img src="file:///android_asset/webkit/android-weberror.png" align="top" />
+            <h2>Pagina d'internet betg disponibla</h2>
+            <p>La pagina d'internet sut <a href="%s">%s</a> &egrave; eventualmain deactivada temporarmain u spustada definitivamain ad ina nova adressa da web.</p>
+
+            <p><b>Qua in p&egrave;r propostas:</b></p>
+            <ul>
+                <li>Controllai che Voss apparat recepescha in signal e dispona d'ina connexiun da datas.</li>
+                <li>Chargiai pli tard danovamain la pagina d'internet.</li>
+                <li>Chargiai ina copia da la pagina da web memorisada en il cache da Google.</li>
+            </ul>
+        </body>
+    </head>
+</html>
diff --git a/core/res/res/values-rm/donottranslate-cldr.xml b/core/res/res/values-rm/donottranslate-cldr.xml
new file mode 100644
index 0000000..4b9f9db
--- /dev/null
+++ b/core/res/res/values-rm/donottranslate-cldr.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="month_long_standalone_january">schaner</string>
+    <string name="month_long_standalone_february">favrer</string>
+    <string name="month_long_standalone_march">mars</string>
+    <string name="month_long_standalone_april">avrigl</string>
+    <string name="month_long_standalone_may">matg</string>
+    <string name="month_long_standalone_june">zercladur</string>
+    <string name="month_long_standalone_july">fanadur</string>
+    <string name="month_long_standalone_august">avust</string>
+    <string name="month_long_standalone_september">settember</string>
+    <string name="month_long_standalone_october">october</string>
+    <string name="month_long_standalone_november">november</string>
+    <string name="month_long_standalone_december">december</string>
+
+    <string name="month_long_january">schaner</string>
+    <string name="month_long_february">favrer</string>
+    <string name="month_long_march">mars</string>
+    <string name="month_long_april">avrigl</string>
+    <string name="month_long_may">matg</string>
+    <string name="month_long_june">zercladur</string>
+    <string name="month_long_july">fanadur</string>
+    <string name="month_long_august">avust</string>
+    <string name="month_long_september">settember</string>
+    <string name="month_long_october">october</string>
+    <string name="month_long_november">november</string>
+    <string name="month_long_december">december</string>
+
+    <string name="month_medium_january">schan.</string>
+    <string name="month_medium_february">favr.</string>
+    <string name="month_medium_march">mars</string>
+    <string name="month_medium_april">avr.</string>
+    <string name="month_medium_may">matg</string>
+    <string name="month_medium_june">zercl.</string>
+    <string name="month_medium_july">fan.</string>
+    <string name="month_medium_august">avust</string>
+    <string name="month_medium_september">sett.</string>
+    <string name="month_medium_october">oct.</string>
+    <string name="month_medium_november">nov.</string>
+    <string name="month_medium_december">dec.</string>
+
+    <string name="month_shortest_january">S</string>
+    <string name="month_shortest_february">F</string>
+    <string name="month_shortest_march">M</string>
+    <string name="month_shortest_april">A</string>
+    <string name="month_shortest_may">M</string>
+    <string name="month_shortest_june">Z</string>
+    <string name="month_shortest_july">F</string>
+    <string name="month_shortest_august">A</string>
+    <string name="month_shortest_september">S</string>
+    <string name="month_shortest_october">O</string>
+    <string name="month_shortest_november">N</string>
+    <string name="month_shortest_december">D</string>
+
+    <string name="sunday">dumengia</string>
+    <string name="monday">glindesdi</string>
+    <string name="tuesday">mardi</string>
+    <string name="wednesday">mesemna</string>
+    <string name="thursday">gievgia</string>
+    <string name="friday">venderdi</string>
+    <string name="saturday">sonda</string>
+
+    <string name="day_of_week_long_sunday">dumengia</string>
+    <string name="day_of_week_long_monday">glindesdi</string>
+    <string name="day_of_week_long_tuesday">mardi</string>
+    <string name="day_of_week_long_wednesday">mesemna</string>
+    <string name="day_of_week_long_thursday">gievgia</string>
+    <string name="day_of_week_long_friday">venderdi</string>
+    <string name="day_of_week_long_saturday">sonda</string>
+
+    <string name="day_of_week_medium_sunday">du</string>
+    <string name="day_of_week_medium_monday">gli</string>
+    <string name="day_of_week_medium_tuesday">ma</string>
+    <string name="day_of_week_medium_wednesday">me</string>
+    <string name="day_of_week_medium_thursday">gie</string>
+    <string name="day_of_week_medium_friday">ve</string>
+    <string name="day_of_week_medium_saturday">so</string>
+
+    <string name="day_of_week_short_sunday">du</string>
+    <string name="day_of_week_short_monday">gli</string>
+    <string name="day_of_week_short_tuesday">ma</string>
+    <string name="day_of_week_short_wednesday">me</string>
+    <string name="day_of_week_short_thursday">gie</string>
+    <string name="day_of_week_short_friday">ve</string>
+    <string name="day_of_week_short_saturday">so</string>
+
+    <string name="day_of_week_shorter_sunday">D</string>
+    <string name="day_of_week_shorter_monday">G</string>
+    <string name="day_of_week_shorter_tuesday">M</string>
+    <string name="day_of_week_shorter_wednesday">M</string>
+    <string name="day_of_week_shorter_thursday">G</string>
+    <string name="day_of_week_shorter_friday">V</string>
+    <string name="day_of_week_shorter_saturday">S</string>
+
+    <string name="day_of_week_shortest_sunday">D</string>
+    <string name="day_of_week_shortest_monday">G</string>
+    <string name="day_of_week_shortest_tuesday">M</string>
+    <string name="day_of_week_shortest_wednesday">M</string>
+    <string name="day_of_week_shortest_thursday">G</string>
+    <string name="day_of_week_shortest_friday">V</string>
+    <string name="day_of_week_shortest_saturday">S</string>
+
+    <string name="yesterday">ier</string>
+    <string name="today">oz</string>
+    <string name="tomorrow">damaun</string>
+
+    <string name="hour_minute_24">%-k:%M</string>
+    <string name="hour_minute_ampm">%-l:%M %p</string>
+    <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
+    <string name="twelve_hour_time_format">h:mm a</string>
+    <string name="twenty_four_hour_time_format">H:mm</string>
+    <string name="numeric_date">%d.%m.%Y</string>
+    <string name="numeric_date_format">dd.MM.yyyy</string>
+    <string name="numeric_date_template">"%s.%s.%s"</string>
+    <string name="month_day_year">%-e. %B %Y</string>
+    <string name="full_date_month_first">d. MMMM y</string>
+    <string name="full_date_day_first">d. MMMM y</string>
+    <string name="medium_date_month_first">d. MMM y</string>
+    <string name="medium_date_day_first">d. MMM y</string>
+    <string name="time_of_day">%H:%M:%S</string>
+    <string name="status_bar_time_format">HH:mm</string>
+    <string name="date_and_time">%H:%M:%S %d.%m.%Y</string>
+    <string name="date_time">%1$s, %2$s</string>
+    <string name="time_date">%3$s, %1$s</string>
+    <string name="abbrev_month_day_year">%d.%m.%Y</string>
+    <string name="month_day">%-e. %B</string>
+    <string name="month">%-B</string>
+    <string name="month_year">%B %Y</string>
+    <string name="abbrev_month_day">%-e. %b</string>
+    <string name="abbrev_month">%-b</string>
+    <string name="abbrev_month_year">%b %Y</string>
+    <string name="time1_time2">%1$s - %2$s</string>
+    <string name="date1_date2">%2$s - %5$s</string>
+    <string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string>
+    <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s. - %6$s, %8$s.%7$s.</string>
+    <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string>
+    <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s</string>
+    <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s-%2$s-%3$s - %10$s %6$s, %9$s-%7$s-%8$s</string>
+    <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s. - %10$s %8$s.%7$s.</string>
+    <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s.</string>
+    <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string>
+    <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string>
+    <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string>
+    <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string>
+    <string name="time_wday_date">%1$s %2$s %3$s</string>
+    <string name="wday_date">%2$s %3$s</string>
+    <string name="time_wday">%1$s %2$s</string>
+    <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string>
+    <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string>
+    <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string>
+    <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string>
+    <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string>
+    <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string>
+    <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string>
+    <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string>
+    <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string>
+    <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string>
+    <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string>
+    <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string>
+    <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string>
+    <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string>
+    <string name="short_format_month">%b</string>
+</resources>
diff --git a/docs/html/guide/topics/fundamentals.jd b/docs/html/guide/topics/fundamentals.jd
index 6d6abd8..db06efc 100644
--- a/docs/html/guide/topics/fundamentals.jd
+++ b/docs/html/guide/topics/fundamentals.jd
@@ -292,8 +292,8 @@
 <li><p>A service is started (or new instructions are given to an ongoing 
 service) by passing an Intent object to <code>{@link 
 android.content.Context#startService Context.startService()}</code>.  
-Android calls the service's <code>{@link android.app.Service#onStart 
-onStart()}</code> method and passes it the Intent object.</p>
+Android calls the service's <code>{@link android.app.Service#onStartCommand
+onStartCommand()}</code> method and passes it the Intent object.</p>
 
 <p>
 Similarly, an intent can be passed to <code>{@link 
@@ -1508,9 +1508,9 @@
 in {@code onCreate()}, and then stop the thread in {@code onDestroy()}.</li>
 
 <li><p>The <b>active lifetime</b> of a service begins with a call to 
-<code>{@link android.app.Service#onStart onStart()}</code>.  This method 
+<code>{@link android.app.Service#onStartCommand onStartCommand()}</code>.  This method
 is handed the Intent object that was passed to {@code startService()}.
-The music service would open the Intent to discover which music to 
+The music service would open the Intent to discover which music to
 play, and begin the playback.</p>
 
 <p>
@@ -1525,7 +1525,7 @@
 <code>{@link android.content.Context#startService Context.startService()}</code>
 or 
 <code>{@link android.content.Context#bindService Context.bindService()}</code>.
-However, {@code onStart()} is called only for services started by {@code
+However, {@code onStartCommand()} is called only for services started by {@code
 startService()}.
 </p>
 
@@ -1629,7 +1629,7 @@
 
 <li><p>It has a {@link android.app.Service} object that's executing
 one of its lifecycle callbacks (<code>{@link android.app.Service#onCreate 
-onCreate()}</code>, <code>{@link android.app.Service#onStart onStart()}</code>, 
+onCreate()}</code>, <code>{@link android.app.Service#onStartCommand onStartCommand()}</code>,
 or <code>{@link android.app.Service#onDestroy onDestroy()}</code>).</p></li>
 
 <li><p>It has a {@link android.content.BroadcastReceiver} object that's 
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index b811479..ed09f89 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -54,16 +54,20 @@
         UNSIGNED_5_5_5_1 (14, 2),
         UNSIGNED_4_4_4_4 (15, 2),
 
-        RS_ELEMENT (16, 4),
-        RS_TYPE (17, 4),
-        RS_ALLOCATION (18, 4),
-        RS_SAMPLER (19, 4),
-        RS_SCRIPT (20, 4),
-        RS_MESH (21, 4),
-        RS_PROGRAM_FRAGMENT (22, 4),
-        RS_PROGRAM_VERTEX (23, 4),
-        RS_PROGRAM_RASTER (24, 4),
-        RS_PROGRAM_STORE (25, 4);
+        MATRIX_4X4 (16, 64),
+        MATRIX_3X3 (17, 36),
+        MATRIX_2X2 (18, 16),
+
+        RS_ELEMENT (1000, 4),
+        RS_TYPE (1001, 4),
+        RS_ALLOCATION (1002, 4),
+        RS_SAMPLER (1003, 4),
+        RS_SCRIPT (1004, 4),
+        RS_MESH (1005, 4),
+        RS_PROGRAM_FRAGMENT (1006, 4),
+        RS_PROGRAM_VERTEX (1007, 4),
+        RS_PROGRAM_RASTER (1008, 4),
+        RS_PROGRAM_STORE (1009, 4);
 
         int mID;
         int mSize;
@@ -285,6 +289,29 @@
         return rs.mElement_UCHAR_4;
     }
 
+    public static Element MATRIX_4X4(RenderScript rs) {
+        if(rs.mElement_MATRIX_4X4 == null) {
+            rs.mElement_MATRIX_4X4 = createUser(rs, DataType.MATRIX_4X4);
+        }
+        return rs.mElement_MATRIX_4X4;
+    }
+    public static Element MATRIX4X4(RenderScript rs) {
+        return MATRIX_4X4(rs);
+    }
+
+    public static Element MATRIX_3X3(RenderScript rs) {
+        if(rs.mElement_MATRIX_3X3 == null) {
+            rs.mElement_MATRIX_3X3 = createUser(rs, DataType.MATRIX_3X3);
+        }
+        return rs.mElement_MATRIX_4X4;
+    }
+
+    public static Element MATRIX_2X2(RenderScript rs) {
+        if(rs.mElement_MATRIX_2X2 == null) {
+            rs.mElement_MATRIX_2X2 = createUser(rs, DataType.MATRIX_2X2);
+        }
+        return rs.mElement_MATRIX_2X2;
+    }
 
     Element(int id, RenderScript rs, Element[] e, String[] n) {
         super(id, rs);
diff --git a/graphics/java/android/renderscript/FieldPacker.java b/graphics/java/android/renderscript/FieldPacker.java
index 24f0409..b6f88be 100644
--- a/graphics/java/android/renderscript/FieldPacker.java
+++ b/graphics/java/android/renderscript/FieldPacker.java
@@ -248,6 +248,24 @@
         addU32(v.w);
     }
 
+    public void addObj(Matrix4f v) {
+        for (int i=0; i < v.mMat.length; i++) {
+            addF32(v.mMat[i]);
+        }
+    }
+
+    public void addObj(Matrix3f v) {
+        for (int i=0; i < v.mMat.length; i++) {
+            addF32(v.mMat[i]);
+        }
+    }
+
+    public void addObj(Matrix2f v) {
+        for (int i=0; i < v.mMat.length; i++) {
+            addF32(v.mMat[i]);
+        }
+    }
+
     public void addBoolean(boolean v) {
         addI8((byte)(v ? 1 : 0));
     }
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index ab1d765..08ba7e2 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -542,6 +542,10 @@
     Element mElement_FLOAT_4;
     Element mElement_UCHAR_4;
 
+    Element mElement_MATRIX_4X4;
+    Element mElement_MATRIX_3X3;
+    Element mElement_MATRIX_2X2;
+
     Sampler mSampler_CLAMP_NEAREST;
     Sampler mSampler_CLAMP_LINEAR;
     Sampler mSampler_CLAMP_LINEAR_MIP_LINEAR;
@@ -614,15 +618,22 @@
             int[] rbuf = new int[16];
             mRS.nContextInitToClient(mRS.mContext);
             while(mRun) {
+                rbuf[0] = 0;
                 int msg = mRS.nContextGetMessage(mRS.mContext, rbuf, true);
                 if (msg == 0) {
-                    // Should only happen during teardown.
-                    // But we want to avoid starving other threads during
-                    // teardown by yielding until the next line in the destructor
-                    // can execute to set mRun = false
-                    try {
-                        sleep(1, 0);
-                    } catch(InterruptedException e) {
+                    // Can happen for two reasons
+                    if (rbuf[0] > 0) {
+                        // 1: Buffer needs to be enlarged.
+                        rbuf = new int[rbuf[0] + 2];
+                    } else {
+                        // 2: teardown.
+                        // But we want to avoid starving other threads during
+                        // teardown by yielding until the next line in the destructor
+                        // can execute to set mRun = false
+                        try {
+                            sleep(1, 0);
+                        } catch(InterruptedException e) {
+                        }
                     }
                 }
                 if(mRS.mMessageCallback != null) {
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index b2e8dd9..81f08e9 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -228,7 +228,8 @@
     size_t receiveLen;
     int id = rsContextGetMessage(con, ptr, &receiveLen, len * 4, wait);
     if (!id && receiveLen) {
-        LOGE("message receive buffer too small.  %i", receiveLen);
+        LOGV("message receive buffer too small.  %i", receiveLen);
+        *ptr = (jint)receiveLen;
     }
     _env->ReleaseIntArrayElements(data, ptr, 0);
     return id;
diff --git a/include/media/EffectPresetReverbApi.h b/include/media/EffectPresetReverbApi.h
index 53205bb..a3f094c 100644
--- a/include/media/EffectPresetReverbApi.h
+++ b/include/media/EffectPresetReverbApi.h
@@ -43,7 +43,8 @@
     REVERB_PRESET_LARGEROOM,
     REVERB_PRESET_MEDIUMHALL,
     REVERB_PRESET_LARGEHALL,
-    REVERB_PRESET_PLATE
+    REVERB_PRESET_PLATE,
+    REVERB_PRESET_LAST = REVERB_PRESET_PLATE
 } t_reverb_presets;
 
 #if __cplusplus
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 6f7dc38..9d2cff6 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -28,6 +28,7 @@
 
 namespace android {
 
+struct AMessage;
 class String8;
 
 class DataSource : public RefBase {
@@ -59,10 +60,14 @@
 
     ////////////////////////////////////////////////////////////////////////////
 
-    bool sniff(String8 *mimeType, float *confidence);
+    bool sniff(String8 *mimeType, float *confidence, sp<AMessage> *meta);
 
+    // The sniffer can optionally fill in "meta" with an AMessage containing
+    // a dictionary of values that helps the corresponding extractor initialize
+    // its state without duplicating effort already exerted by the sniffer.
     typedef bool (*SnifferFunc)(
-            const sp<DataSource> &source, String8 *mimeType, float *confidence);
+            const sp<DataSource> &source, String8 *mimeType,
+            float *confidence, sp<AMessage> *meta);
 
     static void RegisterSniffer(SnifferFunc func);
     static void RegisterDefaultSniffers();
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 3406287..7902b9a 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -91,7 +91,11 @@
     RS_TYPE_UNSIGNED_5_5_5_1,
     RS_TYPE_UNSIGNED_4_4_4_4,
 
-    RS_TYPE_ELEMENT,
+    RS_TYPE_MATRIX_4X4,
+    RS_TYPE_MATRIX_3X3,
+    RS_TYPE_MATRIX_2X2,
+
+    RS_TYPE_ELEMENT = 1000,
     RS_TYPE_TYPE,
     RS_TYPE_ALLOCATION,
     RS_TYPE_SAMPLER,
@@ -100,7 +104,7 @@
     RS_TYPE_PROGRAM_FRAGMENT,
     RS_TYPE_PROGRAM_VERTEX,
     RS_TYPE_PROGRAM_RASTER,
-    RS_TYPE_PROGRAM_STORE
+    RS_TYPE_PROGRAM_STORE,
 };
 
 enum RsDataKind {
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SgTransform.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SgTransform.java
index bfc9bb7..e70e811 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SgTransform.java
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SgTransform.java
@@ -82,15 +82,8 @@
 
     void setData() {
 
-        mTransformData.globalMat_Row0 = new Float4(1, 0, 0, 0);
-        mTransformData.globalMat_Row1 = new Float4(0, 1, 0, 0);
-        mTransformData.globalMat_Row2 = new Float4(0, 0, 1, 0);
-        mTransformData.globalMat_Row3 = new Float4(0, 0, 0, 1);
-
-        mTransformData.localMat_Row0 = new Float4(1, 0, 0, 0);
-        mTransformData.localMat_Row1 = new Float4(0, 1, 0, 0);
-        mTransformData.localMat_Row2 = new Float4(0, 0, 1, 0);
-        mTransformData.localMat_Row3 = new Float4(0, 0, 0, 1);
+        mTransformData.globalMat = new Matrix4f();
+        mTransformData.localMat = new Matrix4f();
 
         mTransformData.transforms0 = mTransforms[0];
         mTransformData.transforms1 = mTransforms[1];
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs
index 8053306..c794438 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs
@@ -76,10 +76,10 @@
     rsgBindProgramStore(gPFSBackground);
     rsgBindTexture(gPFBackground, 0, gTGrid);
 
-    rsgProgramVertexLoadModelMatrix((rs_matrix4x4 *)&robot1Ptr->globalMat_Row0);
+    rsgProgramVertexLoadModelMatrix(&robot1Ptr->globalMat);
     rsgDrawMesh(gTestMesh);
 
-    rsgProgramVertexLoadModelMatrix((rs_matrix4x4 *)&robot2Ptr->globalMat_Row0);
+    rsgProgramVertexLoadModelMatrix(&robot2Ptr->globalMat);
     rsgDrawMesh(gTestMesh);
 
     color(0.3f, 0.3f, 0.3f, 1.0f);
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform.rs
index 7b9cd1c..2ef29cf 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform.rs
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform.rs
@@ -52,8 +52,8 @@
     //rsDebug("Transform data", (int)data);
     //rsDebug("Entering parent", (int)parent);
 
-    rs_matrix4x4 *localMat = (rs_matrix4x4*)&data->localMat_Row0;
-    rs_matrix4x4 *globalMat = (rs_matrix4x4*)&data->globalMat_Row0;
+    rs_matrix4x4 *localMat = &data->localMat;
+    rs_matrix4x4 *globalMat = &data->globalMat;
 
     ParentData toChild;
     toChild.changed = 0;
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh
index a66e6c5..5c872a7 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh
@@ -22,15 +22,8 @@
 #define TRANSFORM_SCALE 3
 
 typedef struct {
-    float4 globalMat_Row0;
-    float4 globalMat_Row1;
-    float4 globalMat_Row2;
-    float4 globalMat_Row3;
-
-    float4 localMat_Row0;
-    float4 localMat_Row1;
-    float4 localMat_Row2;
-    float4 localMat_Row3;
+    rs_matrix4x4 globalMat;
+    rs_matrix4x4 localMat;
 
     float4 transforms0;
     float4 transforms1;
diff --git a/libs/rs/rsComponent.cpp b/libs/rs/rsComponent.cpp
index fbaa75f..f51b23e 100644
--- a/libs/rs/rsComponent.cpp
+++ b/libs/rs/rsComponent.cpp
@@ -95,6 +95,26 @@
         mNormalized = true;
         rsAssert(mKind == RS_KIND_PIXEL_RGBA);
         return;
+
+    case RS_TYPE_MATRIX_4X4:
+        mTypeBits = 16 * 32;
+        rsAssert(mVectorSize == 1);
+        rsAssert(mNormalized == false);
+        rsAssert(mKind == RS_KIND_USER);
+        break;
+    case RS_TYPE_MATRIX_3X3:
+        mTypeBits = 9 * 32;
+        rsAssert(mVectorSize == 1);
+        rsAssert(mNormalized == false);
+        rsAssert(mKind == RS_KIND_USER);
+        break;
+    case RS_TYPE_MATRIX_2X2:
+        mTypeBits = 4 * 32;
+        rsAssert(mVectorSize == 1);
+        rsAssert(mNormalized == false);
+        rsAssert(mKind == RS_KIND_USER);
+        break;
+
     case RS_TYPE_ELEMENT:
     case RS_TYPE_TYPE:
     case RS_TYPE_ALLOCATION:
@@ -218,10 +238,19 @@
         case 4: return String8("vec4");
         }
     }
+    if ((mType == RS_TYPE_MATRIX_4X4) && (mVectorSize == 1)) {
+        return String8("mat4");
+    }
+    if ((mType == RS_TYPE_MATRIX_3X3) && (mVectorSize == 1)) {
+        return String8("mat3");
+    }
+    if ((mType == RS_TYPE_MATRIX_2X2) && (mVectorSize == 1)) {
+        return String8("mat2");
+    }
     return String8();
 }
 
-static const char * gTypeStrings[] = {
+static const char * gTypeBasicStrings[] = {
     "NONE",
     "F16",
     "F32",
@@ -238,6 +267,12 @@
     "UP_565",
     "UP_5551",
     "UP_4444",
+    "MATRIX_4X4",
+    "MATRIX_3X3",
+    "MATRIX_2X2",
+};
+
+static const char * gTypeObjStrings[] = {
     "ELEMENT",
     "TYPE",
     "ALLOCATION",
@@ -267,8 +302,13 @@
 
 void Component::dumpLOGV(const char *prefix) const
 {
-    LOGV("%s   Component: %s, %s, vectorSize=%i, bits=%i",
-         prefix, gTypeStrings[mType], gKindStrings[mKind], mVectorSize, mBits);
+    if (mType >= RS_TYPE_ELEMENT) {
+        LOGV("%s   Component: %s, %s, vectorSize=%i, bits=%i",
+             prefix, gTypeObjStrings[mType - RS_TYPE_ELEMENT], gKindStrings[mKind], mVectorSize, mBits);
+    } else {
+        LOGV("%s   Component: %s, %s, vectorSize=%i, bits=%i",
+             prefix, gTypeBasicStrings[mType], gKindStrings[mKind], mVectorSize, mBits);
+    }
 }
 
 void Component::serialize(OStream *stream) const
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 08dd57b..f67a9d5 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -713,10 +713,10 @@
 uint32_t Context::getMessageToClient(void *data, size_t *receiveLen, size_t bufferLen, bool wait)
 {
     //LOGE("getMessageToClient %i %i", bufferLen, wait);
+    *receiveLen = 0;
     if (!wait) {
         if (mIO.mToClient.isEmpty()) {
             // No message to get and not going to wait for one.
-            receiveLen = 0;
             return 0;
         }
     }
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index 88cce46..7e0d390 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -66,14 +66,6 @@
 
     private final MediaScanner mMediaScanner;
 
-    // MTP property codes
-    private static final int MTP_PROPERTY_STORAGE_ID = 0xDC01;
-    private static final int MTP_PROPERTY_OBJECT_FORMAT = 0xDC02;
-    private static final int MTP_PROPERTY_OBJECT_SIZE = 0xDC04;
-    private static final int MTP_PROPERTY_OBJECT_FILE_NAME = 0xDC07;
-    private static final int MTP_PROPERTY_DATE_MODIFIED = 0xDC09;
-    private static final int MTP_PROPERTY_PARENT_OBJECT = 0xDC0B;
-
     // MTP response codes
     private static final int MTP_RESPONSE_OK = 0x2001;
     private static final int MTP_RESPONSE_GENERAL_ERROR = 0x2002;
@@ -271,25 +263,36 @@
         boolean isString = false;
 
         switch (property) {
-            case MTP_PROPERTY_STORAGE_ID:
+            case Mtp.Object.PROPERTY_STORAGE_ID:
                 outIntValue[0] = mStorageID;
                 return MTP_RESPONSE_OK;
-            case MTP_PROPERTY_OBJECT_FORMAT:
+            case Mtp.Object.PROPERTY_OBJECT_FORMAT:
                 column = MtpObjects.ObjectColumns.FORMAT;
                 break;
-            case MTP_PROPERTY_OBJECT_SIZE:
+            case Mtp.Object.PROPERTY_PROTECTION_STATUS:
+                // protection status is always 0
+                outIntValue[0] = 0;
+                return MTP_RESPONSE_OK;
+            case Mtp.Object.PROPERTY_OBJECT_SIZE:
                 column = MtpObjects.ObjectColumns.SIZE;
                 break;
-            case MTP_PROPERTY_OBJECT_FILE_NAME:
+            case Mtp.Object.PROPERTY_OBJECT_FILE_NAME:
                 column = MtpObjects.ObjectColumns.DATA;
                 isString = true;
                 break;
-            case MTP_PROPERTY_DATE_MODIFIED:
+            case Mtp.Object.PROPERTY_DATE_MODIFIED:
                 column = MtpObjects.ObjectColumns.DATE_MODIFIED;
                 break;
-            case MTP_PROPERTY_PARENT_OBJECT:
+            case Mtp.Object.PROPERTY_PARENT_OBJECT:
                 column = MtpObjects.ObjectColumns.PARENT;
                 break;
+            case Mtp.Object.PROPERTY_PERSISTENT_UID:
+                // PUID is concatenation of storageID and object handle
+                long puid = mStorageID;
+                puid <<= 32;
+                puid += handle;
+                outIntValue[0] = puid;
+                return MTP_RESPONSE_OK;
             default:
                 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
         }
@@ -305,7 +308,7 @@
                     String value = c.getString(1);
                     int start = 0;
 
-                    if (property == MTP_PROPERTY_OBJECT_FILE_NAME) {
+                    if (property == Mtp.Object.PROPERTY_OBJECT_FILE_NAME) {
                         // extract name from full path
                         int lastSlash = value.lastIndexOf('/');
                         if (lastSlash >= 0) {
diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp
index bf83172..4ef3a9f 100644
--- a/media/jni/android_media_MtpDatabase.cpp
+++ b/media/jni/android_media_MtpDatabase.cpp
@@ -29,6 +29,7 @@
 
 #include "MtpDatabase.h"
 #include "MtpDataPacket.h"
+#include "MtpProperty.h"
 #include "MtpUtils.h"
 #include "mtp.h"
 
@@ -45,6 +46,7 @@
 static jmethodID method_getSupportedObjectProperties;
 static jmethodID method_getSupportedDeviceProperties;
 static jmethodID method_getObjectProperty;
+static jmethodID method_setObjectProperty;
 static jmethodID method_getObjectInfo;
 static jmethodID method_getObjectFilePath;
 static jmethodID method_deleteFile;
@@ -98,10 +100,22 @@
     virtual MtpObjectPropertyList*  getSupportedObjectProperties(MtpObjectFormat format);
     virtual MtpDevicePropertyList*  getSupportedDeviceProperties();
 
-    virtual MtpResponseCode         getObjectProperty(MtpObjectHandle handle,
+    virtual MtpResponseCode         getObjectPropertyValue(MtpObjectHandle handle,
                                             MtpObjectProperty property,
                                             MtpDataPacket& packet);
 
+    virtual MtpResponseCode         setObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         getDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         setDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         resetDeviceProperty(MtpDeviceProperty property);
+
     virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
                                             MtpDataPacket& packet);
 
@@ -116,6 +130,11 @@
 
     virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
                                             MtpObjectHandleList* references);
+
+    virtual MtpProperty*            getObjectPropertyDesc(MtpObjectProperty property,
+                                            MtpObjectFormat format);
+
+    virtual MtpProperty*            getDevicePropertyDesc(MtpDeviceProperty property);
 };
 
 // ----------------------------------------------------------------------------
@@ -292,7 +311,7 @@
     return list;
 }
 
-MtpResponseCode MyMtpDatabase::getObjectProperty(MtpObjectHandle handle,
+MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
                                             MtpObjectProperty property,
                                             MtpDataPacket& packet) {
     int         type;
@@ -310,6 +329,15 @@
     jlong longValue = longValues[0];
     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
 
+    // special case MTP_PROPERTY_DATE_MODIFIED, which is a string to MTP
+    // but stored internally as a uint64
+    if (property == MTP_PROPERTY_DATE_MODIFIED) {
+        char    date[20];
+        formatDateTime(longValue, date, sizeof(date));
+        packet.putString(date);
+        return MTP_RESPONSE_OK;
+    }
+
     switch (type) {
         case MTP_TYPE_INT8:
             packet.putInt8(longValue);
@@ -335,6 +363,12 @@
         case MTP_TYPE_UINT64:
             packet.putUInt64(longValue);
             break;
+        case MTP_TYPE_INT128:
+            packet.putInt128(longValue);
+            break;
+        case MTP_TYPE_UINT128:
+            packet.putInt128(longValue);
+            break;
         case MTP_TYPE_STR:
         {
             jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
@@ -351,6 +385,26 @@
     return MTP_RESPONSE_OK;
 }
 
+MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet) {
+    return -1;
+}
+
+MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet) {
+    return -1;
+}
+
+MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet) {
+    return -1;
+}
+
+MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty property) {
+    return -1;
+}
+
 MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
                                             MtpDataPacket& packet) {
     char    date[20];
@@ -372,9 +426,10 @@
     uint64_t modified = longValues[1];
     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
 
-    int associationType = (format == MTP_FORMAT_ASSOCIATION ?
-                            MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
-                            MTP_ASSOCIATION_TYPE_UNDEFINED);
+//    int associationType = (format == MTP_FORMAT_ASSOCIATION ?
+//                            MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
+//                            MTP_ASSOCIATION_TYPE_UNDEFINED);
+    int associationType = MTP_ASSOCIATION_TYPE_UNDEFINED;
 
     packet.putUInt32(storageID);
     packet.putUInt16(format);
@@ -497,6 +552,37 @@
     return result;
 }
 
+MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
+                                            MtpObjectFormat format) {
+    MtpProperty* result = NULL;
+    switch (property) {
+        case MTP_PROPERTY_OBJECT_FORMAT:
+        case MTP_PROPERTY_PROTECTION_STATUS:
+            result = new MtpProperty(property, MTP_TYPE_UINT16);
+            break;
+        case MTP_PROPERTY_STORAGE_ID:
+        case MTP_PROPERTY_PARENT_OBJECT:
+            result = new MtpProperty(property, MTP_TYPE_UINT32);
+            break;
+        case MTP_PROPERTY_OBJECT_SIZE:
+            result = new MtpProperty(property, MTP_TYPE_UINT64);
+            break;
+        case MTP_PROPERTY_PERSISTENT_UID:
+            result = new MtpProperty(property, MTP_TYPE_UINT128);
+            break;
+        case MTP_PROPERTY_OBJECT_FILE_NAME:
+        case MTP_PROPERTY_DATE_MODIFIED:
+            result = new MtpProperty(property, MTP_TYPE_STR);
+            break;
+    }
+
+    return result;
+}
+
+MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
+    return NULL;
+}
+
 #endif // HAVE_ANDROID_OS
 
 // ----------------------------------------------------------------------------
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 798271e..d856eb4 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -17,7 +17,7 @@
 
 #define LOG_TAG "Bundle"
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 
 #include <cutils/log.h>
 #include <assert.h>
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 2043e44..03f1409 100755
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -17,7 +17,7 @@
 
 #define LOG_TAG "Reverb"
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 
 #include <cutils/log.h>
 #include <assert.h>
@@ -61,42 +61,81 @@
 /* Preset definitions                                                               */
 /*                                                                                  */
 /************************************************************************************/
-LVM_UINT16 RevPreset_Level[]    = {  32,   32,   32,   32,   32,    32,   32,   32,   32,   32};
-LVM_UINT16 RevPreset_LPF[]      = {1298, 1000, 5012, 3542, 3400, 23999, 2536, 1000, 1000, 1000};
-LVM_UINT16 RevPreset_HPF[]      = {  50,   50,   50,   50,   50,    50,   50,   50,   50,   50};
-LVM_UINT16 RevPreset_T60[]      = {1490,  500, 2310, 4230, 3920,  2910, 7000, 1490, 1490,  170};
-LVM_UINT16 RevPreset_Density[]  = { 100,  100,  100,  100,  100,   100,  100,  100,  100,  100};
-LVM_UINT16 RevPreset_Damping[]  = {  54,   10,   64,   59,   70,   100,   33,   54,   21,   10};
-LVM_UINT16 RevPreset_RoomSize[] = { 100,  100,  100,  100,  100,   100,  100,  100,  100,  100};
 
-/************************************************************************************/
-/*                                                                                  */
-/* Preset definitions                                                               */
-/*                                                                                  */
-/************************************************************************************/
-#define REV_PRESET_BATHROOM         0
-#define REV_PRESET_LIVINGROOM       1
-#define REV_PRESET_STONEROOM        2
-#define REV_PRESET_AUDITORIUM       3
-#define REV_PRESET_CONCERTHALL      4
-#define REV_PRESET_CAVE             5
-#define REV_PRESET_ARENA            6
-#define REV_PRESET_FOREST           7
-#define REV_PRESET_MOUNTAINS        8
-#define REV_PRESET_PADDEDCELL       9
+const static t_reverb_settings sReverbPresets[] = {
+        // REVERB_PRESET_NONE: values are unused
+        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+        // REVERB_PRESET_SMALLROOM
+        {-1000, -600, 1100, 830, -400, 5, 500, 10, 1000, 1000},
+        // REVERB_PRESET_MEDIUMROOM
+        {-1000, -600, 1300, 830, -1000, 20, -200, 20, 1000, 1000},
+        // REVERB_PRESET_LARGEROOM
+        {-1000, -600, 1500, 830, -1600, 5, -1000, 40, 1000, 1000},
+        // REVERB_PRESET_MEDIUMHALL
+        {-1000, -600, 1800, 700, -1300, 15, -800, 30, 1000, 1000},
+        // REVERB_PRESET_LARGEHALL
+        {-1000, -600, 1800, 700, -2000, 30, -1400, 60, 1000, 1000},
+        // REVERB_PRESET_PLATE
+        {-1000, -200, 1300, 900, 0, 2, 0, 10, 1000, 750},
+};
 
-// NXP SW Reverb UUID
-const effect_descriptor_t gReverbDescriptor = {
+
+// NXP SW auxiliary environmental reverb
+const effect_descriptor_t gAuxEnvReverbDescriptor = {
         { 0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, { 0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e } },
         { 0x4a387fc0, 0x8ab3, 0x11df, 0x8bad, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } },
         EFFECT_API_VERSION,
-        (EFFECT_FLAG_TYPE_AUXILIARY | EFFECT_FLAG_INSERT_LAST),
+        EFFECT_FLAG_TYPE_AUXILIARY,
         0, // TODO
         1,
-        "Reverb",
+        "Auxiliary Environmental Reverb",
         "NXP Software Ltd.",
 };
 
+// NXP SW insert environmental reverb
+static const effect_descriptor_t gInsertEnvReverbDescriptor = {
+        {0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, {0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e}},
+        {0xc7a511a0, 0xa3bb, 0x11df, 0x860e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        EFFECT_API_VERSION,
+        EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+        0, // TODO
+        1,
+        "Insert Environmental Reverb",
+        "NXP Software Ltd.",
+};
+
+// NXP SW auxiliary preset reverb
+static const effect_descriptor_t gAuxPresetReverbDescriptor = {
+        {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        {0xf29a1400, 0xa3bb, 0x11df, 0x8ddc, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        EFFECT_API_VERSION,
+        EFFECT_FLAG_TYPE_AUXILIARY,
+        0, // TODO
+        1,
+        "Auxiliary Preset Reverb",
+        "NXP Software Ltd.",
+};
+
+// NXP SW insert preset reverb
+static const effect_descriptor_t gInsertPresetReverbDescriptor = {
+        {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        {0x172cdf00, 0xa3bc, 0x11df, 0xa72f, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        EFFECT_API_VERSION,
+        EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+        0, // TODO
+        1,
+        "Insert Preset Reverb",
+        "NXP Software Ltd.",
+};
+
+// gDescriptors contains pointers to all defined effect descriptor in this library
+static const effect_descriptor_t * const gDescriptors[] = {
+        &gAuxEnvReverbDescriptor,
+        &gInsertEnvReverbDescriptor,
+        &gAuxPresetReverbDescriptor,
+        &gInsertPresetReverbDescriptor
+};
+
 struct ReverbContext{
     const struct effect_interface_s *itfe;
     effect_config_t                 config;
@@ -114,8 +153,14 @@
     FILE                            *PcmOutPtr;
     #endif
     LVM_Fs_en                       SampleRate;
+    bool                            auxiliary;
+    bool                            preset;
+    uint16_t                        curPreset;
+    uint16_t                        nextPreset;
 };
 
+#define REVERB_DEFAULT_PRESET REVERB_PRESET_MEDIUMROOM
+
 //--- local function prototypes
 int  Reverb_init            (ReverbContext *pContext);
 void Reverb_free            (ReverbContext *pContext);
@@ -125,11 +170,12 @@
                              void          *pParam,
                              size_t        *pValueSize,
                              void          *pValue);
+int Reverb_LoadPreset       (ReverbContext   *pContext);
 
 /* Effect Library Interface Implementation */
 extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects){
     LOGV("\n\tEffectQueryNumberEffects start");
-    *pNumEffects = 1;
+    *pNumEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
     LOGV("\tEffectQueryNumberEffects creating %d effects", *pNumEffects);
     LOGV("\tEffectQueryNumberEffects end\n");
     return 0;
@@ -142,11 +188,11 @@
         LOGV("\tLVM_ERROR : EffectQueryEffect was passed NULL pointer");
         return -EINVAL;
     }
-    if (index > 0){
+    if (index >= sizeof(gDescriptors) / sizeof(const effect_descriptor_t *)) {
         LOGV("\tLVM_ERROR : EffectQueryEffect index out of range %d", index);
         return -ENOENT;
     }
-    memcpy(pDescriptor, &gReverbDescriptor, sizeof(effect_descriptor_t));
+    memcpy(pDescriptor, gDescriptors[index], sizeof(effect_descriptor_t));
     LOGV("\tEffectQueryEffect end\n");
     return 0;
 }     /* end EffectQueryEffect */
@@ -157,6 +203,8 @@
                             effect_interface_t  *pInterface){
     int ret;
     int i;
+    int length = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
+    const effect_descriptor_t *desc;
 
     LOGV("\t\nEffectCreate start");
 
@@ -165,9 +213,16 @@
         return -EINVAL;
     }
 
-    if (memcmp(uuid, &gReverbDescriptor.uuid, sizeof(effect_uuid_t)) != 0){
-        LOGV("\tLVM_ERROR : EffectCreate() invalid UUID");
-        return -EINVAL;
+    for (i = 0; i < length; i++) {
+        desc = gDescriptors[i];
+        if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t))
+                == 0) {
+            break;
+        }
+    }
+
+    if (i == length) {
+        return -ENOENT;
     }
 
     ReverbContext *pContext = new ReverbContext;
@@ -175,6 +230,19 @@
     pContext->itfe      = &gReverbInterface;
     pContext->hInstance = NULL;
 
+    pContext->auxiliary = false;
+    if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY){
+        pContext->auxiliary = true;
+    }
+
+    pContext->preset = false;
+    if (memcmp(&desc->type, SL_IID_PRESETREVERB, sizeof(effect_uuid_t)) == 0) {
+        pContext->preset = true;
+        // force reloading preset at first call to process()
+        pContext->curPreset = REVERB_PRESET_LAST + 1;
+        pContext->nextPreset = REVERB_DEFAULT_PRESET;
+    }
+
     LOGV("\tEffectCreate - Calling Reverb_init");
     ret = Reverb_init(pContext);
 
@@ -288,6 +356,14 @@
 
    return;
 }
+
+static inline int16_t clamp16(int32_t sample)
+{
+    if ((sample>>15) ^ (sample>>31))
+        sample = 0x7FFF ^ (sample>>31);
+    return sample;
+}
+
 //----------------------------------------------------------------------------
 // process()
 //----------------------------------------------------------------------------
@@ -344,6 +420,9 @@
     fflush(pContext->PcmInPtr);
     #endif
 
+    if (pContext->preset && pContext->nextPreset != pContext->curPreset) {
+        Reverb_LoadPreset(pContext);
+    }
     // Convert to Input 32 bits
     for(int i=0; i<frameCount*samplesPerFrame; i++){
         InFrames32[i] = (LVM_INT32)pIn[i]<<8;
@@ -359,18 +438,28 @@
     //frameCount, pContext->config.inputCfg.channels, CHANNEL_MONO,
     //pContext->config.outputCfg.channels, CHANNEL_STEREO);
 
+    if (pContext->preset && pContext->curPreset == REVERB_PRESET_NONE) {
+        memset(OutFrames32, 0, frameCount * sizeof(LVM_INT32) * 2);
+    } else {
     /* Process the samples */
     LvmStatus = LVREV_Process(pContext->hInstance,      /* Instance handle */
                               InFrames32,               /* Input buffer */
                               OutFrames32,              /* Output buffer */
                               frameCount);              /* Number of samples to read */
+    }
+
+    if (!pContext->auxiliary) {
+        for (int i=0; i<frameCount*2; i++){
+            OutFrames32[i] += InFrames32[i];
+        }
+    }
 
     LVM_ERROR_CHECK(LvmStatus, "LVREV_Process", "process")
     if(LvmStatus != LVREV_SUCCESS) return -EINVAL;
 
     // Convert to 16 bits
     for(int i=0; i<frameCount*2; i++){  // Always stereo
-        OutFrames16[i] = (LVM_INT16)(OutFrames32[i]>>8);
+        OutFrames16[i] = clamp16(OutFrames32[i]>>8);
     }
 
     #ifdef LVM_PCM
@@ -382,7 +471,7 @@
     if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){
         //LOGV("\tBuffer access is ACCUMULATE");
         for (int i=0; i<frameCount*2; i++){
-            pOut[i] +=  OutFrames16[i];
+            pOut[i] = clamp16((int32_t)pOut[i] + (int32_t)OutFrames16[i]);
         }
     }else{
         //LOGV("\tBuffer access is WRITE");
@@ -462,6 +551,8 @@
 
     CHECK_ARG(pConfig->inputCfg.samplingRate == pConfig->outputCfg.samplingRate);
     CHECK_ARG(pConfig->inputCfg.format == pConfig->outputCfg.format);
+    CHECK_ARG((pContext->auxiliary && pConfig->inputCfg.channels == CHANNEL_MONO) ||
+              ((!pContext->auxiliary) && pConfig->inputCfg.channels == CHANNEL_STEREO));
     CHECK_ARG(pConfig->outputCfg.channels == CHANNEL_STEREO);
     CHECK_ARG(pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE
               || pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
@@ -540,18 +631,8 @@
 
 int Reverb_init(ReverbContext *pContext){
     int status;
-    int channel_mode;
 
-    LOGV("\tReverb_init start %d", gReverbDescriptor.flags);
-
-    if((gReverbDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT){
-        LOGV("\tReverb_init EFFECT_FLAG_TYPE_INSERT");
-        channel_mode = CHANNEL_STEREO;
-    }
-    if((gReverbDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY ){
-        LOGV("\tReverb_init EFFECT_FLAG_TYPE_AUXILIARY");
-        channel_mode = CHANNEL_MONO;
-    }
+    LOGV("\tReverb_init start");
 
     CHECK_ARG(pContext != NULL);
 
@@ -560,7 +641,12 @@
     }
 
     pContext->config.inputCfg.accessMode                    = EFFECT_BUFFER_ACCESS_READ;
-    pContext->config.inputCfg.channels                      = channel_mode;
+    if (pContext->auxiliary) {
+        pContext->config.inputCfg.channels                  = CHANNEL_MONO;
+    } else {
+        pContext->config.inputCfg.channels                  = CHANNEL_STEREO;
+    }
+
     pContext->config.inputCfg.format                        = SAMPLE_FORMAT_PCM_S15;
     pContext->config.inputCfg.samplingRate                  = 44100;
     pContext->config.inputCfg.bufferProvider.getBuffer      = NULL;
@@ -653,11 +739,11 @@
     /* Reverb parameters */
     params.Level          = 0;
     params.LPF            = 23999;
-    params.HPF            = RevPreset_HPF[REV_PRESET_MOUNTAINS];
-    params.T60            = RevPreset_T60[REV_PRESET_MOUNTAINS];
-    params.Density        = RevPreset_Density[REV_PRESET_MOUNTAINS];
-    params.Damping        = RevPreset_Damping[REV_PRESET_MOUNTAINS];
-    params.RoomSize       = RevPreset_RoomSize[REV_PRESET_MOUNTAINS];
+    params.HPF            = 50;
+    params.T60            = 1490;
+    params.Density        = 100;
+    params.Damping        = 21;
+    params.RoomSize       = 100;
 
     /* Saved strength is used to return the exact strength that was used in the set to the get
      * because we map the original strength range of 0:1000 to 1:15, and this will avoid
@@ -1294,6 +1380,44 @@
 }
 
 //----------------------------------------------------------------------------
+// Reverb_LoadPreset()
+//----------------------------------------------------------------------------
+// Purpose:
+// Load a the next preset
+//
+// Inputs:
+//  pContext         - handle to instance data
+//
+// Outputs:
+//
+// Side Effects:
+//
+//----------------------------------------------------------------------------
+int Reverb_LoadPreset(ReverbContext   *pContext)
+{
+    //TODO: add reflections delay, level and reverb delay when early reflections are
+    // implemented
+    pContext->curPreset = pContext->nextPreset;
+
+    if (pContext->curPreset != REVERB_PRESET_NONE) {
+        const t_reverb_settings *preset = &sReverbPresets[pContext->curPreset];
+        ReverbSetRoomLevel(pContext, preset->roomLevel);
+        ReverbSetRoomHfLevel(pContext, preset->roomHFLevel);
+        ReverbSetDecayTime(pContext, preset->decayTime);
+        ReverbSetDecayHfRatio(pContext, preset->decayHFRatio);
+        //reflectionsLevel
+        //reflectionsDelay
+        ReverbSetReverbLevel(pContext, preset->reverbLevel);
+        // reverbDelay
+        ReverbSetDiffusion(pContext, preset->diffusion);
+        ReverbSetDensity(pContext, preset->density);
+    }
+
+    return 0;
+}
+
+
+//----------------------------------------------------------------------------
 // Reverb_getParameter()
 //----------------------------------------------------------------------------
 // Purpose:
@@ -1325,6 +1449,15 @@
     t_reverb_settings *pProperties;
 
     //LOGV("\tReverb_getParameter start");
+    if (pContext->preset) {
+        if (param != REVERB_PARAM_PRESET || *pValueSize < sizeof(uint16_t)) {
+            return -EINVAL;
+        }
+
+        *(uint16_t *)pValue = pContext->nextPreset;
+        LOGV("get REVERB_PARAM_PRESET, preset %d", pContext->nextPreset);
+        return 0;
+    }
 
     switch (param){
         case REVERB_PARAM_ROOM_LEVEL:
@@ -1531,6 +1664,18 @@
     int32_t param = *pParamTemp++;
 
     //LOGV("\tReverb_setParameter start");
+    if (pContext->preset) {
+        if (param != REVERB_PARAM_PRESET) {
+            return -EINVAL;
+        }
+
+        uint16_t preset = *(uint16_t *)pValue;
+        LOGV("set REVERB_PARAM_PRESET, preset %d", preset);
+        if (preset > REVERB_PRESET_LAST) {
+            return -EINVAL;
+        }
+        pContext->nextPreset = preset;
+    }
 
     switch (param){
         case REVERB_PARAM_PROPERTIES:
diff --git a/media/libmedia/fixedfft.cpp b/media/libmedia/fixedfft.cpp
index 28eb05a..9cf05ba 100644
--- a/media/libmedia/fixedfft.cpp
+++ b/media/libmedia/fixedfft.cpp
@@ -26,7 +26,9 @@
 
 #include <stdio.h>
 #include <stdint.h>
+#ifdef __ARM_ARCH__
 #include <machine/cpu-features.h>
+#endif
 
 #define LOG_FFT_SIZE 10
 #define MAX_FFT_SIZE (1 << LOG_FFT_SIZE)
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index 70af2da..1b05528 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -87,7 +87,7 @@
       mInitCheck(NO_INIT) {
     String8 mimeType;
     float confidence;
-    if (!SniffAMR(mDataSource, &mimeType, &confidence)) {
+    if (!SniffAMR(mDataSource, &mimeType, &confidence, NULL)) {
         return;
     }
 
@@ -276,7 +276,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool SniffAMR(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     char header[9];
 
     if (source->readAt(0, header, sizeof(header)) != sizeof(header)) {
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index e167afa..1ccfa03 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -461,27 +461,34 @@
         return;
     }
 
+    bool eos;
+    size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&eos);
+
     size_t lowWatermark = 400000;
     size_t highWatermark = 1000000;
 
-    off_t size;
-    if (mDurationUs >= 0 && mCachedSource->getSize(&size) == OK) {
-        int64_t bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
+    if (eos) {
+        notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
+    } else {
+        off_t size;
+        if (mDurationUs >= 0 && mCachedSource->getSize(&size) == OK) {
+            int64_t bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
 
-        size_t cachedSize = mCachedSource->cachedSize();
-        int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate;
+            size_t cachedSize = mCachedSource->cachedSize();
+            int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate;
 
-        double percentage = (double)cachedDurationUs / mDurationUs;
+            int percentage = 100.0 * (double)cachedDurationUs / mDurationUs;
+            if (percentage > 100) {
+                percentage = 100;
+            }
 
-        notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage * 100.0);
+            notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage);
 
-        lowWatermark = 2 * bitrate / 8;  // 2 secs
-        highWatermark = 10 * bitrate / 8;  // 10 secs
+            lowWatermark = 2 * bitrate / 8;  // 2 secs
+            highWatermark = 10 * bitrate / 8;  // 10 secs
+        }
     }
 
-    bool eos;
-    size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&eos);
-
     if ((mFlags & PLAYING) && !eos && (cachedDataRemaining < lowWatermark)) {
         LOGI("cache is running low (< %d) , pausing.", lowWatermark);
         mFlags |= CACHE_UNDERRUN;
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 90a596c..49eac62 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -25,6 +25,7 @@
 
 #include "matroska/MatroskaExtractor.h"
 
+#include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/FileSource.h>
 #include <media/stagefright/MediaErrors.h>
@@ -56,19 +57,23 @@
 Mutex DataSource::gSnifferMutex;
 List<DataSource::SnifferFunc> DataSource::gSniffers;
 
-bool DataSource::sniff(String8 *mimeType, float *confidence) {
+bool DataSource::sniff(
+        String8 *mimeType, float *confidence, sp<AMessage> *meta) {
     *mimeType = "";
     *confidence = 0.0f;
+    meta->clear();
 
     Mutex::Autolock autoLock(gSnifferMutex);
     for (List<SnifferFunc>::iterator it = gSniffers.begin();
          it != gSniffers.end(); ++it) {
         String8 newMimeType;
         float newConfidence;
-        if ((*it)(this, &newMimeType, &newConfidence)) {
+        sp<AMessage> newMeta;
+        if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) {
             if (newConfidence > *confidence) {
                 *mimeType = newMimeType;
                 *confidence = newConfidence;
+                *meta = newMeta;
             }
         }
     }
@@ -92,13 +97,13 @@
 
 // static
 void DataSource::RegisterDefaultSniffers() {
-    RegisterSniffer(SniffMP3);
     RegisterSniffer(SniffMPEG4);
-    RegisterSniffer(SniffAMR);
-    RegisterSniffer(SniffWAV);
-    RegisterSniffer(SniffOgg);
     RegisterSniffer(SniffMatroska);
+    RegisterSniffer(SniffOgg);
+    RegisterSniffer(SniffWAV);
+    RegisterSniffer(SniffAMR);
     RegisterSniffer(SniffMPEG2TS);
+    RegisterSniffer(SniffMP3);
 }
 
 // static
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 4058fbc..2e36968 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -22,6 +22,7 @@
 
 #include "include/ID3.h"
 
+#include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
@@ -456,15 +457,31 @@
     MP3Source &operator=(const MP3Source &);
 };
 
-MP3Extractor::MP3Extractor(const sp<DataSource> &source)
+MP3Extractor::MP3Extractor(
+        const sp<DataSource> &source, const sp<AMessage> &meta)
     : mDataSource(source),
       mFirstFramePos(-1),
       mFixedHeader(0),
       mByteNumber(0) {
     off_t pos = 0;
     uint32_t header;
-    bool success = Resync(mDataSource, 0, &pos, &header);
-    CHECK(success);
+    bool success;
+
+    int64_t meta_offset;
+    uint32_t meta_header;
+    if (meta != NULL
+            && meta->findInt64("offset", &meta_offset)
+            && meta->findInt32("header", (int32_t *)&meta_header)) {
+        // The sniffer has already done all the hard work for us, simply
+        // accept its judgement.
+        pos = (off_t)meta_offset;
+        header = meta_header;
+
+        success = true;
+    } else {
+        success = Resync(mDataSource, 0, &pos, &header);
+        CHECK(success);
+    }
 
     if (success) {
         mFirstFramePos = pos;
@@ -759,15 +776,20 @@
 }
 
 bool SniffMP3(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType,
+        float *confidence, sp<AMessage> *meta) {
     off_t pos = 0;
     uint32_t header;
     if (!Resync(source, 0, &pos, &header)) {
         return false;
     }
 
+    *meta = new AMessage;
+    (*meta)->setInt64("offset", pos);
+    (*meta)->setInt32("header", header);
+
     *mimeType = MEDIA_MIMETYPE_AUDIO_MPEG;
-    *confidence = 0.3f;
+    *confidence = 0.2f;
 
     return true;
 }
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 12a1e6e..ba90407 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1738,7 +1738,7 @@
         || !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8)
         || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)) {
         *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
-        *confidence = 0.1;
+        *confidence = 0.4;
 
         return true;
     }
@@ -1805,13 +1805,14 @@
     }
 
     *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
-    *confidence = 0.3f;
+    *confidence = 0.4f;
 
     return true;
 }
 
 bool SniffMPEG4(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     if (BetterSniffMPEG4(source, mimeType, confidence)) {
         return true;
     }
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index e36d9fe..a15b84e 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -1569,9 +1569,9 @@
                 // The idea here is to avoid having two or more samples with the
                 // same timestamp in the output file.
                 if (mTimeScale >= 1000000LL) {
-                    timestampUs += 1;
+                    timestampUs = lastTimestampUs + 1;
                 } else {
-                    timestampUs += (1000000LL + (mTimeScale >> 1)) / mTimeScale;
+                    timestampUs = lastTimestampUs + (1000000LL + (mTimeScale >> 1)) / mTimeScale;
                 }
 #endif
             }
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 56e6136..9bc94de 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -27,6 +27,7 @@
 
 #include "matroska/MatroskaExtractor.h"
 
+#include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaExtractor.h>
@@ -46,10 +47,12 @@
 // static
 sp<MediaExtractor> MediaExtractor::Create(
         const sp<DataSource> &source, const char *mime) {
+    sp<AMessage> meta;
+
     String8 tmp;
     if (mime == NULL) {
         float confidence;
-        if (!source->sniff(&tmp, &confidence)) {
+        if (!source->sniff(&tmp, &confidence, &meta)) {
             LOGV("FAILED to autodetect media content.");
 
             return NULL;
@@ -64,7 +67,7 @@
             || !strcasecmp(mime, "audio/mp4")) {
         return new MPEG4Extractor(source);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
-        return new MP3Extractor(source);
+        return new MP3Extractor(source, meta);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)
             || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
         return new AMRExtractor(source);
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 9630092..2c1311a 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -804,7 +804,8 @@
 }
 
 bool SniffOgg(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     char tmp[4];
     if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) {
         return false;
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 8d820c0..57c1075 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -404,7 +404,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool SniffWAV(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     char header[12];
     if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) {
         return false;
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index ca0c68c..da340f7 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -142,11 +142,22 @@
     mSize = size;
 
     if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) {
+        free(mData);
+        mData = NULL;
+
         return false;
     }
 
-    if (header.flags & 0x80) {
+    if (header.version_major == 4) {
+        if (!removeUnsynchronizationV2_4()) {
+            free(mData);
+            mData = NULL;
+
+            return false;
+        }
+    } else if (header.flags & 0x80) {
         LOGV("removing unsynchronization");
+
         removeUnsynchronization();
     }
 
@@ -243,6 +254,74 @@
     }
 }
 
+static void WriteSyncsafeInteger(uint8_t *dst, size_t x) {
+    for (size_t i = 0; i < 4; ++i) {
+        dst[3 - i] = (x & 0x7f);
+        x >>= 7;
+    }
+}
+
+bool ID3::removeUnsynchronizationV2_4() {
+    size_t oldSize = mSize;
+
+    size_t offset = 0;
+    while (offset + 10 <= mSize) {
+        if (!memcmp(&mData[offset], "\0\0\0\0", 4)) {
+            break;
+        }
+
+        size_t dataSize;
+        if (!ParseSyncsafeInteger(&mData[offset + 4], &dataSize)) {
+            return false;
+        }
+
+        if (offset + dataSize + 10 > mSize) {
+            return false;
+        }
+
+        uint16_t flags = U16_AT(&mData[offset + 8]);
+        uint16_t prevFlags = flags;
+
+        if (flags & 1) {
+            // Strip data length indicator
+
+            memmove(&mData[offset + 10], &mData[offset + 14], mSize - offset - 14);
+            mSize -= 4;
+            dataSize -= 4;
+
+            flags &= ~1;
+        }
+
+        if (flags & 2) {
+            // Unsynchronization added.
+
+            for (size_t i = 0; i + 1 < dataSize; ++i) {
+                if (mData[offset + 10 + i] == 0xff
+                        && mData[offset + 11 + i] == 0x00) {
+                    memmove(&mData[offset + 11 + i], &mData[offset + 12 + i],
+                            mSize - offset - 12 - i);
+                    --mSize;
+                    --dataSize;
+                }
+            }
+
+            flags &= ~2;
+        }
+
+        if (flags != prevFlags) {
+            WriteSyncsafeInteger(&mData[offset + 4], dataSize);
+            mData[offset + 8] = flags >> 8;
+            mData[offset + 9] = flags & 0xff;
+        }
+
+        offset += 10 + dataSize;
+    }
+
+    memset(&mData[mSize], 0, oldSize - mSize);
+
+    return true;
+}
+
 ID3::Iterator::Iterator(const ID3 &parent, const char *id)
     : mParent(parent),
       mID(NULL),
@@ -529,10 +608,11 @@
 
             uint16_t flags = U16_AT(&mParent.mData[mOffset + 8]);
 
-            if ((mParent.mVersion == ID3_V2_4 && (flags & 0x000e))
+            if ((mParent.mVersion == ID3_V2_4 && (flags & 0x000c))
                 || (mParent.mVersion == ID3_V2_3 && (flags & 0x00c0))) {
-                // Compression, Encryption or per-Frame unsynchronization
-                // are not supported at this time.
+                // Compression or encryption are not supported at this time.
+                // Per-frame unsynchronization and data-length indicator
+                // have already been taken care of.
 
                 LOGV("Skipping unsupported frame (compression, encryption "
                      "or per-frame unsynchronization flagged");
diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h
index db49fe4..1cdf36d 100644
--- a/media/libstagefright/include/AMRExtractor.h
+++ b/media/libstagefright/include/AMRExtractor.h
@@ -22,6 +22,7 @@
 
 namespace android {
 
+struct AMessage;
 class String8;
 
 class AMRExtractor : public MediaExtractor {
@@ -49,7 +50,8 @@
 };
 
 bool SniffAMR(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h
index c6b1a8b..7ddbb41 100644
--- a/media/libstagefright/include/ID3.h
+++ b/media/libstagefright/include/ID3.h
@@ -80,6 +80,7 @@
     bool parseV1(const sp<DataSource> &source);
     bool parseV2(const sp<DataSource> &source);
     void removeUnsynchronization();
+    bool removeUnsynchronizationV2_4();
 
     static bool ParseSyncsafeInteger(const uint8_t encoded[4], size_t *x);
 
diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h
index 3ce6df3..0e6ccde 100644
--- a/media/libstagefright/include/MP3Extractor.h
+++ b/media/libstagefright/include/MP3Extractor.h
@@ -22,13 +22,14 @@
 
 namespace android {
 
+struct AMessage;
 class DataSource;
 class String8;
 
 class MP3Extractor : public MediaExtractor {
 public:
     // Extractor assumes ownership of "source".
-    MP3Extractor(const sp<DataSource> &source);
+    MP3Extractor(const sp<DataSource> &source, const sp<AMessage> &meta);
 
     virtual size_t countTracks();
     virtual sp<MediaSource> getTrack(size_t index);
@@ -52,7 +53,8 @@
 };
 
 bool SniffMP3(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *meta);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index c96973b..1bf4cd1 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -9,6 +9,7 @@
 
 namespace android {
 
+struct AMessage;
 struct AnotherPacketSource;
 struct ATSParser;
 struct DataSource;
@@ -47,7 +48,8 @@
 };
 
 bool SniffMPEG2TS(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index c8663d5..1c9cc7e 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -23,6 +23,7 @@
 
 namespace android {
 
+struct AMessage;
 class DataSource;
 class SampleTable;
 class String8;
@@ -75,7 +76,8 @@
 };
 
 bool SniffMPEG4(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/OggExtractor.h b/media/libstagefright/include/OggExtractor.h
index 7066669..1eda025 100644
--- a/media/libstagefright/include/OggExtractor.h
+++ b/media/libstagefright/include/OggExtractor.h
@@ -22,6 +22,7 @@
 
 namespace android {
 
+struct AMessage;
 class DataSource;
 class String8;
 
@@ -53,7 +54,8 @@
 };
 
 bool SniffOgg(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h
index 3e847b9..df6d3e7 100644
--- a/media/libstagefright/include/WAVExtractor.h
+++ b/media/libstagefright/include/WAVExtractor.h
@@ -22,6 +22,7 @@
 
 namespace android {
 
+struct AMessage;
 class DataSource;
 class String8;
 
@@ -58,7 +59,8 @@
 };
 
 bool SniffWAV(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 71f6587..7c7d69e 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -579,7 +579,8 @@
 }
 
 bool SniffMatroska(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
     DataSourceReader reader(source);
     mkvparser::EBMLHeader ebmlHeader;
     long long pos;
diff --git a/media/libstagefright/matroska/MatroskaExtractor.h b/media/libstagefright/matroska/MatroskaExtractor.h
index 7471848..fa20b84 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.h
+++ b/media/libstagefright/matroska/MatroskaExtractor.h
@@ -27,6 +27,7 @@
 
 namespace android {
 
+struct AMessage;
 class String8;
 
 struct DataSourceReader;
@@ -69,7 +70,8 @@
 };
 
 bool SniffMatroska(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence);
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
 
 }  // namespace android
 
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index b287c95..56ca375 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -174,7 +174,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool SniffMPEG2TS(
-        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
 #if 0
     char header;
     if (source->readAt(0, &header, 1) != 1 || header != 0x47) {
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 27dc796..ec78ff0 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -242,6 +242,16 @@
     putUInt32(value[3]);
 }
 
+void MtpDataPacket::putInt128(int64_t value) {
+    putInt64(value);
+    putInt64(value < 0 ? -1 : 0);
+}
+
+void MtpDataPacket::putUInt128(uint64_t value) {
+    putUInt64(value);
+    putUInt64(0);
+}
+
 void MtpDataPacket::putAInt8(const int8_t* values, int count) {
     putUInt32(count);
     for (int i = 0; i < count; i++)
@@ -363,7 +373,7 @@
 int MtpDataPacket::write(int fd) {
     MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
     MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
-
+    dump();
     // send header separately from data
     int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE);
     if (ret == MTP_CONTAINER_HEADER_SIZE)
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
index 1467aab..fab6a07 100644
--- a/media/mtp/MtpDataPacket.h
+++ b/media/mtp/MtpDataPacket.h
@@ -69,6 +69,8 @@
     void                putUInt64(uint64_t value);
     void                putInt128(const int128_t& value);
     void                putUInt128(const uint128_t& value);
+    void                putInt128(int64_t value);
+    void                putUInt128(uint64_t value);
 
     void                putAInt8(const int8_t* values, int count);
     void                putAUInt8(const uint8_t* values, int count);
diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h
index 17823df..899b34a 100644
--- a/media/mtp/MtpDatabase.h
+++ b/media/mtp/MtpDatabase.h
@@ -22,6 +22,7 @@
 namespace android {
 
 class MtpDataPacket;
+class MtpProperty;
 
 class MtpDatabase {
 public:
@@ -58,10 +59,22 @@
     virtual MtpObjectPropertyList*  getSupportedObjectProperties(MtpObjectFormat format) = 0;
     virtual MtpDevicePropertyList*  getSupportedDeviceProperties() = 0;
 
-    virtual MtpResponseCode         getObjectProperty(MtpObjectHandle handle,
+    virtual MtpResponseCode         getObjectPropertyValue(MtpObjectHandle handle,
                                             MtpObjectProperty property,
                                             MtpDataPacket& packet) = 0;
 
+    virtual MtpResponseCode         setObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         getDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         setDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         resetDeviceProperty(MtpDeviceProperty property) = 0;
+
     virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
                                             MtpDataPacket& packet) = 0;
 
@@ -76,6 +89,10 @@
     virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
                                             MtpObjectHandleList* references) = 0;
 
+    virtual MtpProperty*            getObjectPropertyDesc(MtpObjectProperty property,
+                                            MtpObjectFormat format) = 0;
+
+    virtual MtpProperty*            getDevicePropertyDesc(MtpDeviceProperty property) = 0;
 };
 
 }; // namespace android
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 30abfb8..1e64e4f 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -56,17 +56,17 @@
 //    MTP_OPERATION_SET_OBJECT_PROTECTION,
 //    MTP_OPERATION_POWER_DOWN,
 //    MTP_OPERATION_GET_DEVICE_PROP_DESC,
-//    MTP_OPERATION_GET_DEVICE_PROP_VALUE,
-//    MTP_OPERATION_SET_DEVICE_PROP_VALUE,
-//    MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
+    MTP_OPERATION_GET_DEVICE_PROP_VALUE,
+    MTP_OPERATION_SET_DEVICE_PROP_VALUE,
+    MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
 //    MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
 //    MTP_OPERATION_MOVE_OBJECT,
 //    MTP_OPERATION_COPY_OBJECT,
 //    MTP_OPERATION_GET_PARTIAL_OBJECT,
 //    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
     MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
-//    MTP_OPERATION_GET_OBJECT_PROP_DESC,
-    MTP_OPERATION_GET_OBJECT_PROP_VALUE,
+    MTP_OPERATION_GET_OBJECT_PROP_DESC,
+//    MTP_OPERATION_GET_OBJECT_PROP_VALUE,
 //    MTP_OPERATION_SET_OBJECT_PROP_VALUE,
     MTP_OPERATION_GET_OBJECT_REFERENCES,
     MTP_OPERATION_SET_OBJECT_REFERENCES,
@@ -91,7 +91,6 @@
         mSendObjectFormat(0),
         mSendObjectFileSize(0)
 {
-    initObjectProperties();
 }
 
 MtpServer::~MtpServer() {
@@ -136,7 +135,9 @@
 
         // FIXME need to generalize this
         bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
-                    || operation == MTP_OPERATION_SET_OBJECT_REFERENCES);
+                    || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
+                    || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
+                    || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
         if (dataIn) {
             int ret = mData.read(fd);
             if (ret < 0) {
@@ -158,7 +159,6 @@
                 mData.setOperationCode(operation);
                 mData.setTransactionID(transaction);
                 LOGV("sending data:");
-                mData.dump();
                 ret = mData.write(fd);
                 if (ret < 0) {
                     LOGE("request write returned %d, errno: %d", ret, errno);
@@ -187,24 +187,6 @@
     }
 }
 
-MtpProperty* MtpServer::getObjectProperty(MtpPropertyCode propCode) {
-    for (int i = 0; i < mObjectProperties.size(); i++) {
-        MtpProperty* property = mObjectProperties[i];
-        if (property->getPropertyCode() == propCode)
-            return property;
-    }
-    return NULL;
-}
-
-MtpProperty* MtpServer::getDeviceProperty(MtpPropertyCode propCode) {
-    for (int i = 0; i < mDeviceProperties.size(); i++) {
-        MtpProperty* property = mDeviceProperties[i];
-        if (property->getPropertyCode() == propCode)
-            return property;
-    }
-    return NULL;
-}
-
 void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
     if (mSessionOpen) {
         LOGD("sendObjectAdded %d\n", handle);
@@ -227,14 +209,6 @@
     }
 }
 
-void MtpServer::initObjectProperties() {
-    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32));
-    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16));
-    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64));
-    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR));
-    mObjectProperties.push(new MtpProperty(MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32));
-}
-
 bool MtpServer::handleRequest() {
     MtpOperationCode operation = mRequest.getOperationCode();
     MtpResponseCode response;
@@ -281,6 +255,18 @@
         case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
             response = doGetObjectPropValue();
             break;
+        case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
+            response = doSetObjectPropValue();
+            break;
+        case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
+            response = doGetDevicePropValue();
+            break;
+        case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
+            response = doSetDevicePropValue();
+            break;
+        case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
+            response = doResetDevicePropValue();
+            break;
         case MTP_OPERATION_GET_OBJECT_INFO:
             response = doGetObjectInfo();
             break;
@@ -456,13 +442,15 @@
     if (!mSessionOpen)
         return MTP_RESPONSE_SESSION_NOT_OPEN;
     MtpStorageID handle = mRequest.getParameter(1);
+
+    // FIXME - check for invalid object handle
     MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
-    if (!handles) {
+    if (handles) {
+        mData.putAUInt32(handles);
+        delete handles;
+    } else {
         mData.putEmptyArray();
-        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
     }
-    mData.putAUInt32(handles);
-    delete handles;
     return MTP_RESPONSE_OK;
 }
 
@@ -479,8 +467,43 @@
 MtpResponseCode MtpServer::doGetObjectPropValue() {
     MtpObjectHandle handle = mRequest.getParameter(1);
     MtpObjectProperty property = mRequest.getParameter(2);
+    LOGD("GetObjectPropValue %d %s\n", handle,
+            MtpDebug::getObjectPropCodeName(property));
 
-    return mDatabase->getObjectProperty(handle, property, mData);
+    return mDatabase->getObjectPropertyValue(handle, property, mData);
+}
+
+MtpResponseCode MtpServer::doSetObjectPropValue() {
+    MtpObjectHandle handle = mRequest.getParameter(1);
+    MtpObjectProperty property = mRequest.getParameter(2);
+    LOGD("SetObjectPropValue %d %s\n", handle,
+            MtpDebug::getObjectPropCodeName(property));
+
+    return mDatabase->setObjectPropertyValue(handle, property, mData);
+}
+
+MtpResponseCode MtpServer::doGetDevicePropValue() {
+    MtpDeviceProperty property = mRequest.getParameter(1);
+    LOGD("GetDevicePropValue %s\n",
+            MtpDebug::getDevicePropCodeName(property));
+
+    return mDatabase->getDevicePropertyValue(property, mData);
+}
+
+MtpResponseCode MtpServer::doSetDevicePropValue() {
+    MtpDeviceProperty property = mRequest.getParameter(1);
+    LOGD("SetDevicePropValue %s\n",
+            MtpDebug::getDevicePropCodeName(property));
+
+    return mDatabase->setDevicePropertyValue(property, mData);
+}
+
+MtpResponseCode MtpServer::doResetDevicePropValue() {
+    MtpDeviceProperty property = mRequest.getParameter(1);
+    LOGD("ResetDevicePropValue %s\n",
+            MtpDebug::getDevicePropCodeName(property));
+
+    return mDatabase->resetDeviceProperty(property);
 }
 
 MtpResponseCode MtpServer::doGetObjectInfo() {
@@ -592,7 +615,7 @@
     }
 
     mResponse.setParameter(1, storageID);
-    mResponse.setParameter(2, (parent == 0 ? 0xFFFFFFFF: parent));
+    mResponse.setParameter(2, parent);
     mResponse.setParameter(3, handle);
 
     return MTP_RESPONSE_OK;
@@ -677,11 +700,24 @@
 MtpResponseCode MtpServer::doGetObjectPropDesc() {
     MtpObjectProperty propCode = mRequest.getParameter(1);
     MtpObjectFormat format = mRequest.getParameter(2);
-    MtpProperty* property = getObjectProperty(propCode);
+    LOGD("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
+                                        MtpDebug::getFormatCodeName(format));
+    MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
     if (!property)
         return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
-
     property->write(mData);
+    delete property;
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetDevicePropDesc() {
+    MtpDeviceProperty propCode = mRequest.getParameter(1);
+    LOGD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
+    MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
+    if (!property)
+        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+    property->write(mData);
+    delete property;
     return MTP_RESPONSE_OK;
 }
 
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index 19ccf24..68a6564 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -28,7 +28,6 @@
 namespace android {
 
 class MtpDatabase;
-class MtpProperty;
 class MtpStorage;
 
 class MtpServer {
@@ -57,9 +56,6 @@
 
     MtpStorageList      mStorages;
 
-    MtpPropertyList     mObjectProperties;
-    MtpPropertyList     mDeviceProperties;
-
     // handle for new object, set by SendObjectInfo and used by SendObject
     MtpObjectHandle     mSendObjectHandle;
     MtpObjectFormat     mSendObjectFormat;
@@ -76,15 +72,10 @@
     MtpStorage*         getStorage(MtpStorageID id);
     void                run();
 
-    MtpProperty*        getObjectProperty(MtpPropertyCode propCode);
-    MtpProperty*        getDeviceProperty(MtpPropertyCode propCode);
-
     void                sendObjectAdded(MtpObjectHandle handle);
     void                sendObjectRemoved(MtpObjectHandle handle);
 
 private:
-    void                initObjectProperties();
-
     bool                handleRequest();
 
     MtpResponseCode     doGetDeviceInfo();
@@ -98,12 +89,17 @@
     MtpResponseCode     doGetObjectReferences();
     MtpResponseCode     doSetObjectReferences();
     MtpResponseCode     doGetObjectPropValue();
+    MtpResponseCode     doSetObjectPropValue();
+    MtpResponseCode     doGetDevicePropValue();
+    MtpResponseCode     doSetDevicePropValue();
+    MtpResponseCode     doResetDevicePropValue();
     MtpResponseCode     doGetObjectInfo();
     MtpResponseCode     doGetObject();
     MtpResponseCode     doSendObjectInfo();
     MtpResponseCode     doSendObject();
     MtpResponseCode     doDeleteObject();
     MtpResponseCode     doGetObjectPropDesc();
+    MtpResponseCode     doGetDevicePropDesc();
 };
 
 }; // namespace android
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
index c7f461e..46135ff 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
@@ -27,7 +27,9 @@
 import com.android.mediaframeworktest.functional.MediaAudioManagerTest;
 import com.android.mediaframeworktest.functional.MediaAudioEffectTest;
 import com.android.mediaframeworktest.functional.MediaBassBoostTest;
+import com.android.mediaframeworktest.functional.MediaEnvReverbTest;
 import com.android.mediaframeworktest.functional.MediaEqualizerTest;
+import com.android.mediaframeworktest.functional.MediaPresetReverbTest;
 import com.android.mediaframeworktest.functional.MediaVirtualizerTest;
 import com.android.mediaframeworktest.functional.MediaVisualizerTest;
 import junit.framework.TestSuite;
@@ -62,7 +64,9 @@
         suite.addTestSuite(MediaAudioManagerTest.class);
         suite.addTestSuite(MediaAudioEffectTest.class);
         suite.addTestSuite(MediaBassBoostTest.class);
+        suite.addTestSuite(MediaEnvReverbTest.class);
         suite.addTestSuite(MediaEqualizerTest.class);
+        suite.addTestSuite(MediaPresetReverbTest.class);
         suite.addTestSuite(MediaVirtualizerTest.class);
         suite.addTestSuite(MediaVisualizerTest.class);
         return suite;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java
new file mode 100644
index 0000000..d339e06
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import android.media.Visualizer;
+import android.util.Log;
+
+/**
+ * The EnergyProbe class provides audio signal energy measurements based on the FFT returned
+ * by the Visualizer class. The measure is qualitative and not quantitative in that the returned
+ * value has no unit and is just proportional to the amount of energy present around the
+ * specified frequency.
+ */
+
+public class EnergyProbe {
+    private String TAG = "EnergyProbe";
+
+    private static int CAPTURE_SIZE = 1024;
+    private static int MEASURE_COUNT = 5;
+    private static int AVERAGE_COUNT = 3;
+
+    private Visualizer mVisualizer = null;
+    private int mMaxFrequency = 0;
+    private int mCapturePeriodMs;
+    private byte[] mFft = new byte[CAPTURE_SIZE];
+
+    public EnergyProbe(int session) {
+        try {
+            mVisualizer = new Visualizer(session);
+            if (mVisualizer != null) {
+                mVisualizer.setCaptureSize(CAPTURE_SIZE);
+                mMaxFrequency = mVisualizer.getSamplingRate() / 2000;
+                mCapturePeriodMs = 1000000 / mVisualizer.getMaxCaptureRate();
+            }
+        } catch (UnsupportedOperationException e) {
+            Log.e(TAG, "Error creating visualizer");
+        } catch (IllegalStateException e) {
+            Log.e(TAG, "Error configuring visualizer");
+        }
+    }
+
+    public int capture(int freq) throws InterruptedException {
+        int energy = 0;
+        int count = 0;
+
+        if (freq > mMaxFrequency) {
+            return 0;
+        }
+
+        if (mVisualizer != null) {
+            try {
+                mVisualizer.setEnabled(true);
+                for (int i = 0; i < MEASURE_COUNT; i++) {
+                    if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
+                        if (freq == mMaxFrequency) {
+                            energy += (int)mFft[0] * (int)mFft[0];
+                        } else {
+                            int bin = 2 * (freq * CAPTURE_SIZE / mMaxFrequency / 2);
+                            if (bin < 2) bin = 2;
+                            int tmp = 0;
+                            int j;
+                            for (j = 0;
+                                 (j < AVERAGE_COUNT) && ((bin + 2 * j) < CAPTURE_SIZE);
+                                 j++) {
+                                tmp += (int)mFft[bin + 2 * j] * (int)mFft[bin + 2 * j] +
+                                       (int)mFft[bin + 2 * j + 1] * (int)mFft[bin + 2 * j + 1];
+                            }
+                            // j is always != 0
+                            energy += tmp/j;
+                        }
+                        count++;
+                    }
+                    Thread.sleep(mCapturePeriodMs);
+                }
+                mVisualizer.setEnabled(false);
+            } catch (IllegalStateException e) {
+                Log.e(TAG, "Error capturing audio");
+            }
+        }
+        if (count == 0) {
+            return 0;
+        }
+        return energy/count;
+    }
+
+    public void release() {
+        if (mVisualizer != null) {
+            mVisualizer.release();
+            mVisualizer = null;
+        }
+    }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
index 8a68c5e..aca729e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
@@ -43,7 +43,7 @@
  */
 public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
     private String TAG = "MediaBassBoostTest";
-    private final static int MIN_ENERGY_RATIO_2 = 4;
+    private final static int MIN_ENERGY_RATIO_2 = 3;
     private final static short TEST_STRENGTH = 500;
 
     private BassBoost mBassBoost = null;
@@ -259,52 +259,6 @@
     // private methods
     //----------------------------------
 
-    private class EnergyProbe {
-        Visualizer mVisualizer = null;
-        private byte[] mFft = new byte[1024];
-
-        public EnergyProbe(int session) {
-            mVisualizer = new Visualizer(session);
-            mVisualizer.setCaptureSize(1024);
-        }
-
-        public int capture(int freq) throws InterruptedException {
-            int energy = 0;
-            int count = 0;
-            if (mVisualizer != null) {
-                mVisualizer.setEnabled(true);
-                for (int i = 0; i < 10; i++) {
-                    if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
-                        // TODO: check speex FFT as it seems to return only the number of points
-                        // correspondong to valid part of the spectrum (< Fs).
-                        // e.g., if the number of points is 1024, it covers the frequency range
-                        // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
-                        int bin = freq / (22050 / 1024);
-                        int tmp = 0;
-                        for (int j = bin-2; j < bin+3; j++) {
-                            tmp += (int)mFft[j] * (int)mFft[j];
-                        }
-                        energy += tmp/5;
-                        count++;
-                    }
-                    Thread.sleep(50);
-                }
-                mVisualizer.setEnabled(false);
-            }
-            if (count == 0) {
-                return 0;
-            }
-            return energy/count;
-        }
-
-        public void release() {
-            if (mVisualizer != null) {
-                mVisualizer.release();
-                mVisualizer = null;
-            }
-        }
-    }
-
     private void getBassBoost(int session) {
          if (mBassBoost == null || session != mSession) {
              if (session != mSession && mBassBoost != null) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java
new file mode 100644
index 0000000..db0db70
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.EnvironmentalReverb;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaEnvReverbTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+    private String TAG = "MediaEnvReverbTest";
+    // allow +/- 100 millibel difference between set and get gains
+    private final static int MILLIBEL_TOLERANCE = 100;
+    // allow +/- 5% tolerance between set and get delays
+    private final static float DELAY_TOLERANCE = 1.05f;
+    // allow +/- 5% tolerance between set and get ratios
+    private final static float RATIO_TOLERANCE = 1.05f;
+
+    private EnvironmentalReverb mReverb = null;
+    private int mSession = -1;
+
+    public MediaEnvReverbTest() {
+        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+      super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        releaseReverb();
+    }
+
+    private static void assumeTrue(String message, boolean cond) {
+        assertTrue("(assume)"+message, cond);
+    }
+
+    private void log(String testName, String message) {
+        Log.v(TAG, "["+testName+"] "+message);
+    }
+
+    private void loge(String testName, String message) {
+        Log.e(TAG, "["+testName+"] "+message);
+    }
+
+    //-----------------------------------------------------------------
+    // ENVIRONMENTAL REVEB TESTS:
+    //----------------------------------
+
+
+    //-----------------------------------------------------------------
+    // 0 - constructor
+    //----------------------------------
+
+    //Test case 0.0: test constructor and release
+    @LargeTest
+    public void test0_0ConstructorAndRelease() throws Exception {
+        boolean result = false;
+        String msg = "test1_0ConstructorAndRelease()";
+        EnvironmentalReverb reverb = null;
+         try {
+            reverb = new EnvironmentalReverb(0, 0);
+            assertNotNull(msg + ": could not create EnvironmentalReverb", reverb);
+            try {
+                assertTrue(msg +": invalid effect ID", (reverb.getId() != 0));
+            } catch (IllegalStateException e) {
+                msg = msg.concat(": EnvironmentalReverb not initialized");
+            }
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": EnvironmentalReverb not found");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": Effect library not loaded");
+        } finally {
+            if (reverb != null) {
+                reverb.release();
+            }
+        }
+        assertTrue(msg, result);
+    }
+
+    //-----------------------------------------------------------------
+    // 1 - get/set parameters
+    //----------------------------------
+
+    //Test case 1.0: test room level and room HF level
+    @LargeTest
+    public void test1_0Room() throws Exception {
+        boolean result = false;
+        String msg = "test1_0Room()";
+        getReverb(0);
+        try {
+            mReverb.setRoomLevel((short)0);
+            short level = mReverb.getRoomLevel();
+            assertTrue(msg +": got incorrect room level",
+                    (level > (0 - MILLIBEL_TOLERANCE)) &&
+                    (level < (0 + MILLIBEL_TOLERANCE)));
+
+            mReverb.setRoomHFLevel((short)-6);
+            level = mReverb.getRoomHFLevel();
+            assertTrue(msg +": got incorrect room HF level",
+                    (level > (-6 - MILLIBEL_TOLERANCE)) &&
+                    (level < (-6 + MILLIBEL_TOLERANCE)));
+
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+        assertTrue(msg, result);
+    }
+
+    //Test case 1.1: test decay time and ratio
+    @LargeTest
+    public void test1_1Decay() throws Exception {
+        boolean result = false;
+        String msg = "test1_1Decay()";
+        getReverb(0);
+        try {
+            mReverb.setDecayTime(500);
+            int time = mReverb.getDecayTime();
+            assertTrue(msg +": got incorrect decay time",
+                    ((float)time > (float)(500 / DELAY_TOLERANCE)) &&
+                    ((float)time < (float)(500 * DELAY_TOLERANCE)));
+
+            mReverb.setDecayHFRatio((short)1000);
+            short ratio = mReverb.getDecayHFRatio();
+            assertTrue(msg +": got incorrect decay HF ratio",
+                    ((float)ratio > (float)(1000 / RATIO_TOLERANCE)) &&
+                    ((float)ratio < (float)(1000 * RATIO_TOLERANCE)));
+
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+        assertTrue(msg, result);
+    }
+
+    //Test case 1.2: test reflections
+    @LargeTest
+    public void test1_2Reflections() throws Exception {
+        // TODO: uncomment when early reflections are implemented
+//        boolean result = false;
+//        String msg = "test1_2Reflections()";
+//        getReverb(0);
+//        try {
+//            mReverb.setReflectionsLevel((short)0);
+//            short level = mReverb.getReflectionsLevel();
+//            assertTrue(msg +": got incorrect reflections level",
+//                    (level > (0 - MILLIBEL_TOLERANCE)) &&
+//                    (level < (0 + MILLIBEL_TOLERANCE)));
+//
+//            mReverb.setReflectionsDelay(30);
+//            int delay = mReverb.getReflectionsDelay();
+//            assertTrue(msg +": got incorrect reflections delay",
+//                    ((float)delay > (float)(30 / DELAY_TOLERANCE)) &&
+//                    ((float)delay < (float)(30 * DELAY_TOLERANCE)));
+//
+//            result = true;
+//        } catch (IllegalArgumentException e) {
+//            msg = msg.concat(": Bad parameter value");
+//            loge(msg, "Bad parameter value");
+//        } catch (UnsupportedOperationException e) {
+//            msg = msg.concat(": get parameter() rejected");
+//            loge(msg, "get parameter() rejected");
+//        } catch (IllegalStateException e) {
+//            msg = msg.concat("get parameter() called in wrong state");
+//            loge(msg, "get parameter() called in wrong state");
+//        } finally {
+//            releaseReverb();
+//        }
+//        assertTrue(msg, result);
+    }
+
+    //Test case 1.3: test reverb
+    @LargeTest
+    public void test1_3Reverb() throws Exception {
+        boolean result = false;
+        String msg = "test1_3Reverb()";
+        getReverb(0);
+        try {
+            mReverb.setReverbLevel((short)0);
+            short level = mReverb.getReverbLevel();
+            assertTrue(msg +": got incorrect reverb level",
+                    (level > (0 - MILLIBEL_TOLERANCE)) &&
+                    (level < (0 + MILLIBEL_TOLERANCE)));
+
+            // TODO: change delay when early reflections are implemented
+            mReverb.setReverbDelay(0);
+            int delay = mReverb.getReverbDelay();
+            assertTrue(msg +": got incorrect reverb delay", delay < 5);
+
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+        assertTrue(msg, result);
+    }
+
+    //Test case 1.4: test diffusion and density
+    @LargeTest
+    public void test1_4DiffusionAndDensity() throws Exception {
+        boolean result = false;
+        String msg = "test1_4DiffusionAndDensity()";
+        getReverb(0);
+        try {
+            mReverb.setDiffusion((short)500);
+            short diffusion = mReverb.getDiffusion();
+            assertTrue(msg +": got incorrect diffusion",
+                    ((float)diffusion > (float)(500 / RATIO_TOLERANCE)) &&
+                    ((float)diffusion < (float)(500 * RATIO_TOLERANCE)));
+
+            mReverb.setDensity((short)500);
+            short density = mReverb.getDensity();
+            assertTrue(msg +": got incorrect density",
+                    ((float)density > (float)(500 / RATIO_TOLERANCE)) &&
+                    ((float)density < (float)(500 * RATIO_TOLERANCE)));
+
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+        assertTrue(msg, result);
+    }
+
+    //Test case 1.5: test properties
+    @LargeTest
+    public void test1_5Properties() throws Exception {
+        boolean result = false;
+        String msg = "test1_5Properties()";
+        getReverb(0);
+        try {
+            EnvironmentalReverb.Settings settings = mReverb.getProperties();
+            short newRoomLevel = 0;
+            if (settings.roomLevel == 0) {
+                newRoomLevel = -1000;
+            }
+            String str = settings.toString();
+            settings = new EnvironmentalReverb.Settings(str);
+            settings.roomLevel = newRoomLevel;
+            mReverb.setProperties(settings);
+            settings = mReverb.getProperties();
+            assertTrue(msg +": setProperties failed",
+                    (settings.roomLevel > (newRoomLevel - MILLIBEL_TOLERANCE)) &&
+                    (settings.roomLevel < (newRoomLevel + MILLIBEL_TOLERANCE)));
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+        assertTrue(msg, result);
+    }
+
+    //-----------------------------------------------------------------
+    // 2 - Effect action
+    //----------------------------------
+
+    //Test case 2.0: test actual auxiliary reverb influence on sound
+    @LargeTest
+    public void test2_0AuxiliarySoundModification() throws Exception {
+        boolean result = false;
+        String msg = "test2_0AuxiliarySoundModification()";
+        EnergyProbe probe = null;
+        AudioEffect vc = null;
+        MediaPlayer mp = null;
+        AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+        am.setStreamVolume(AudioManager.STREAM_MUSIC,
+                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+                           0);
+        getReverb(0);
+        try {
+            probe = new EnergyProbe(0);
+            // creating a volume controller on output mix ensures that ro.audio.silent mutes
+            // audio after the effects and not before
+            vc = new AudioEffect(
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+                      0,
+                      0);
+            vc.setEnabled(true);
+
+            mp = new MediaPlayer();
+            mp.setDataSource(MediaNames.SINE_200_1000);
+            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            mp.attachAuxEffect(mReverb.getId());
+            mp.setAuxEffectSendLevel(1.0f);
+            mReverb.setRoomLevel((short)0);
+            mReverb.setReverbLevel((short)0);
+            mReverb.setDecayTime(2000);
+            mReverb.setEnabled(true);
+            mp.prepare();
+            mp.start();
+            Thread.sleep(1000);
+            mp.stop();
+            Thread.sleep(300);
+            // measure energy around 1kHz after media player was stopped for 300 ms
+            int energy1000 = probe.capture(1000);
+            assertTrue(msg + ": reverb has no effect", energy1000 > 0);
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } catch (InterruptedException e) {
+            loge(msg, "sleep() interrupted");
+        }
+        finally {
+            releaseReverb();
+            if (mp != null) {
+                mp.release();
+            }
+            if (vc != null) {
+                vc.release();
+            }
+            if (probe != null) {
+                probe.release();
+            }
+            am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+        }
+        assertTrue(msg, result);
+    }
+
+    //Test case 2.1: test actual insert reverb influence on sound
+    @LargeTest
+    public void test2_1InsertSoundModification() throws Exception {
+        boolean result = false;
+        String msg = "test2_1InsertSoundModification()";
+        EnergyProbe probe = null;
+        AudioEffect vc = null;
+        MediaPlayer mp = null;
+        AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+        am.setStreamVolume(AudioManager.STREAM_MUSIC,
+                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+                           0);
+        try {
+            probe = new EnergyProbe(0);
+            // creating a volume controller on output mix ensures that ro.audio.silent mutes
+            // audio after the effects and not before
+            vc = new AudioEffect(
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+                      0,
+                      0);
+            vc.setEnabled(true);
+
+            mp = new MediaPlayer();
+            mp.setDataSource(MediaNames.SINE_200_1000);
+            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            getReverb(mp.getAudioSessionId());
+            mReverb.setRoomLevel((short)0);
+            mReverb.setReverbLevel((short)0);
+            mReverb.setDecayTime(2000);
+            mReverb.setEnabled(true);
+            mp.prepare();
+            mp.start();
+            Thread.sleep(1000);
+            mp.stop();
+            Thread.sleep(300);
+            // measure energy around 1kHz after media player was stopped for 300 ms
+            int energy1000 = probe.capture(1000);
+            assertTrue(msg + ": reverb has no effect", energy1000 > 0);
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } catch (InterruptedException e) {
+            loge(msg, "sleep() interrupted");
+        }
+        finally {
+            releaseReverb();
+            if (mp != null) {
+                mp.release();
+            }
+            if (vc != null) {
+                vc.release();
+            }
+            if (probe != null) {
+                probe.release();
+            }
+            am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+        }
+        assertTrue(msg, result);
+    }
+
+    //-----------------------------------------------------------------
+    // private methods
+    //----------------------------------
+
+    private void getReverb(int session) {
+         if (mReverb == null || session != mSession) {
+             if (session != mSession && mReverb != null) {
+                 mReverb.release();
+                 mReverb = null;
+             }
+             try {
+                mReverb = new EnvironmentalReverb(0, session);
+                mSession = session;
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "getReverb() EnvironmentalReverb not found exception: "+e);
+            } catch (UnsupportedOperationException e) {
+                Log.e(TAG, "getReverb() Effect library not loaded exception: "+e);
+            }
+         }
+         assertNotNull("could not create mReverb", mReverb);
+    }
+
+    private void releaseReverb() {
+        if (mReverb != null) {
+            mReverb.release();
+            mReverb = null;
+        }
+   }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
index e46887b..7b3945d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
@@ -322,52 +322,6 @@
     // private methods
     //----------------------------------
 
-    private class EnergyProbe {
-        Visualizer mVisualizer = null;
-        private byte[] mFft = new byte[1024];
-
-        public EnergyProbe(int session) {
-            mVisualizer = new Visualizer(session);
-            mVisualizer.setCaptureSize(1024);
-        }
-
-        public int capture(int freq) throws InterruptedException {
-            int energy = 0;
-            int count = 0;
-            if (mVisualizer != null) {
-                mVisualizer.setEnabled(true);
-                for (int i = 0; i < 10; i++) {
-                    if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
-                        // TODO: check speex FFT as it seems to return only the number of points
-                        // correspondong to valid part of the spectrum (< Fs).
-                        // e.g., if the number of points is 1024, it covers the frequency range
-                        // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
-                        int bin = freq / (22050 / 1024);
-                        int tmp = 0;
-                        for (int j = bin-2; j < bin+3; j++) {
-                            tmp += (int)mFft[j] * (int)mFft[j];
-                        }
-                        energy += tmp/5;
-                        count++;
-                    }
-                    Thread.sleep(50);
-                }
-                mVisualizer.setEnabled(false);
-            }
-            if (count == 0) {
-                return 0;
-            }
-            return energy/count;
-        }
-
-        public void release() {
-            if (mVisualizer != null) {
-                mVisualizer.release();
-                mVisualizer = null;
-            }
-        }
-    }
-
     private void getEqualizer(int session) {
          if (mEqualizer == null || session != mSession) {
              if (session != mSession && mEqualizer != null) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java
new file mode 100644
index 0000000..c14319a
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.PresetReverb;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaPresetReverbTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+    private String TAG = "MediaPresetReverbTest";
+
+    private PresetReverb mReverb = null;
+    private int mSession = -1;
+
+    public MediaPresetReverbTest() {
+        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+      super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        releaseReverb();
+    }
+
+    private static void assumeTrue(String message, boolean cond) {
+        assertTrue("(assume)"+message, cond);
+    }
+
+    private void log(String testName, String message) {
+        Log.v(TAG, "["+testName+"] "+message);
+    }
+
+    private void loge(String testName, String message) {
+        Log.e(TAG, "["+testName+"] "+message);
+    }
+
+    //-----------------------------------------------------------------
+    // PRESET REVEB TESTS:
+    //----------------------------------
+
+
+    //-----------------------------------------------------------------
+    // 0 - constructor
+    //----------------------------------
+
+    //Test case 0.0: test constructor and release
+    @LargeTest
+    public void test0_0ConstructorAndRelease() throws Exception {
+        boolean result = false;
+        String msg = "test1_0ConstructorAndRelease()";
+        PresetReverb reverb = null;
+         try {
+            reverb = new PresetReverb(0, 0);
+            assertNotNull(msg + ": could not create PresetReverb", reverb);
+            try {
+                assertTrue(msg +": invalid effect ID", (reverb.getId() != 0));
+            } catch (IllegalStateException e) {
+                msg = msg.concat(": PresetReverb not initialized");
+            }
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": PresetReverb not found");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": Effect library not loaded");
+        } finally {
+            if (reverb != null) {
+                reverb.release();
+            }
+        }
+        assertTrue(msg, result);
+    }
+
+    //-----------------------------------------------------------------
+    // 1 - get/set parameters
+    //----------------------------------
+
+    //Test case 1.0: test preset
+    @LargeTest
+    public void test1_0Preset() throws Exception {
+        boolean result = false;
+        String msg = "test1_0Preset()";
+        getReverb(0);
+        try {
+            mReverb.setPreset((short)PresetReverb.PRESET_LARGEROOM);
+            short preset = mReverb.getPreset();
+            assertEquals(msg +": got incorrect preset",
+                         (short)PresetReverb.PRESET_LARGEROOM,
+                         preset);
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+        assertTrue(msg, result);
+    }
+
+    //Test case 1.1: test properties
+    @LargeTest
+    public void test1_1Properties() throws Exception {
+        boolean result = false;
+        String msg = "test1_1Properties()";
+        getReverb(0);
+        try {
+            PresetReverb.Settings settings = mReverb.getProperties();
+            short newPreset = (short)PresetReverb.PRESET_LARGEROOM;
+            if (settings.preset == (short)PresetReverb.PRESET_LARGEROOM) {
+                newPreset = (short)PresetReverb.PRESET_SMALLROOM;
+            }
+            String str = settings.toString();
+            settings = new PresetReverb.Settings(str);
+            settings.preset = newPreset;
+            mReverb.setProperties(settings);
+            settings = mReverb.getProperties();
+            assertEquals(msg +": setProperties failed", newPreset, settings.preset);
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } finally {
+            releaseReverb();
+        }
+        assertTrue(msg, result);
+    }
+
+    //-----------------------------------------------------------------
+    // 2 - Effect action
+    //----------------------------------
+
+    //Test case 2.0: test actual auxiliary reverb influence on sound
+    @LargeTest
+    public void test2_0AuxiliarySoundModification() throws Exception {
+        boolean result = false;
+        String msg = "test2_0AuxiliarySoundModification()";
+        EnergyProbe probe = null;
+        AudioEffect vc = null;
+        MediaPlayer mp = null;
+        AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+        am.setStreamVolume(AudioManager.STREAM_MUSIC,
+                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+                           0);
+        getReverb(0);
+        try {
+            probe = new EnergyProbe(0);
+            // creating a volume controller on output mix ensures that ro.audio.silent mutes
+            // audio after the effects and not before
+            vc = new AudioEffect(
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+                      0,
+                      0);
+            vc.setEnabled(true);
+
+            mp = new MediaPlayer();
+            mp.setDataSource(MediaNames.SINE_200_1000);
+            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            mp.attachAuxEffect(mReverb.getId());
+            mp.setAuxEffectSendLevel(1.0f);
+            mReverb.setPreset((short)PresetReverb.PRESET_PLATE);
+            mReverb.setEnabled(true);
+            mp.prepare();
+            mp.start();
+            Thread.sleep(1000);
+            mp.stop();
+            Thread.sleep(200);
+            // measure energy around 1kHz after media player was stopped for 200 ms
+            int energy1000 = probe.capture(1000);
+            assertTrue(msg + ": reverb has no effect", energy1000 > 0);
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } catch (InterruptedException e) {
+            loge(msg, "sleep() interrupted");
+        }
+        finally {
+            releaseReverb();
+            if (mp != null) {
+                mp.release();
+            }
+            if (vc != null) {
+                vc.release();
+            }
+            if (probe != null) {
+                probe.release();
+            }
+            am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+        }
+        assertTrue(msg, result);
+    }
+
+    //Test case 2.1: test actual insert reverb influence on sound
+    @LargeTest
+    public void test2_1InsertSoundModification() throws Exception {
+        boolean result = false;
+        String msg = "test2_1InsertSoundModification()";
+        EnergyProbe probe = null;
+        AudioEffect vc = null;
+        MediaPlayer mp = null;
+        AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+        am.setStreamVolume(AudioManager.STREAM_MUSIC,
+                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+                           0);
+        try {
+            probe = new EnergyProbe(0);
+            // creating a volume controller on output mix ensures that ro.audio.silent mutes
+            // audio after the effects and not before
+            vc = new AudioEffect(
+                    AudioEffect.EFFECT_TYPE_NULL,
+                    UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+                      0,
+                      0);
+            vc.setEnabled(true);
+
+            mp = new MediaPlayer();
+            mp.setDataSource(MediaNames.SINE_200_1000);
+            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            getReverb(mp.getAudioSessionId());
+            mReverb.setPreset((short)PresetReverb.PRESET_PLATE);
+            mReverb.setEnabled(true);
+            mp.prepare();
+            mp.start();
+            Thread.sleep(1000);
+            mp.stop();
+            Thread.sleep(200);
+            // measure energy around 1kHz after media player was stopped for 200 ms
+            int energy1000 = probe.capture(1000);
+            assertTrue(msg + ": reverb has no effect", energy1000 > 0);
+            result = true;
+        } catch (IllegalArgumentException e) {
+            msg = msg.concat(": Bad parameter value");
+            loge(msg, "Bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            msg = msg.concat(": get parameter() rejected");
+            loge(msg, "get parameter() rejected");
+        } catch (IllegalStateException e) {
+            msg = msg.concat("get parameter() called in wrong state");
+            loge(msg, "get parameter() called in wrong state");
+        } catch (InterruptedException e) {
+            loge(msg, "sleep() interrupted");
+        }
+        finally {
+            releaseReverb();
+            if (mp != null) {
+                mp.release();
+            }
+            if (vc != null) {
+                vc.release();
+            }
+            if (probe != null) {
+                probe.release();
+            }
+            am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+        }
+        assertTrue(msg, result);
+    }
+
+    //-----------------------------------------------------------------
+    // private methods
+    //----------------------------------
+
+    private void getReverb(int session) {
+         if (mReverb == null || session != mSession) {
+             if (session != mSession && mReverb != null) {
+                 mReverb.release();
+                 mReverb = null;
+             }
+             try {
+                mReverb = new PresetReverb(0, session);
+                mSession = session;
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "getReverb() PresetReverb not found exception: "+e);
+            } catch (UnsupportedOperationException e) {
+                Log.e(TAG, "getReverb() Effect library not loaded exception: "+e);
+            }
+         }
+         assertNotNull("could not create mReverb", mReverb);
+    }
+
+    private void releaseReverb() {
+        if (mReverb != null) {
+            mReverb.release();
+            mReverb = null;
+        }
+   }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
index 6b8ae44..517d575 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
@@ -43,7 +43,7 @@
  */
 public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
     private String TAG = "MediaVirtualizerTest";
-    private final static int MIN_ENERGY_RATIO_2 = 4;
+    private final static int MIN_ENERGY_RATIO_2 = 3;
     private final static short TEST_STRENGTH = 500;
 
     private Virtualizer mVirtualizer = null;
@@ -224,7 +224,7 @@
             int energy200 = probe.capture(200);
             int energy1000 = probe.capture(1000);
             // verify that the energy ration between low and high frequencies is at least
-            // four times higher with virtualizer on.
+            // MIN_ENERGY_RATIO_2 times higher with virtualizer on.
             // NOTE: this is what is observed with current virtualizer implementation and the test
             // audio file but is not the primary effect of the virtualizer. A better way would
             // be to have a stereo PCM capture and check that a strongly paned input is centered
@@ -264,52 +264,6 @@
     // private methods
     //----------------------------------
 
-    private class EnergyProbe {
-        Visualizer mVisualizer = null;
-        private byte[] mFft = new byte[1024];
-
-        public EnergyProbe(int session) {
-            mVisualizer = new Visualizer(session);
-            mVisualizer.setCaptureSize(1024);
-        }
-
-        public int capture(int freq) throws InterruptedException {
-            int energy = 0;
-            int count = 0;
-            if (mVisualizer != null) {
-                mVisualizer.setEnabled(true);
-                for (int i = 0; i < 10; i++) {
-                    if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
-                        // TODO: check speex FFT as it seems to return only the number of points
-                        // correspondong to valid part of the spectrum (< Fs).
-                        // e.g., if the number of points is 1024, it covers the frequency range
-                        // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
-                        int bin = freq / (22050 / 1024);
-                        int tmp = 0;
-                        for (int j = bin-2; j < bin+3; j++) {
-                            tmp += (int)mFft[j] * (int)mFft[j];
-                        }
-                        energy += tmp/5;
-                        count++;
-                    }
-                    Thread.sleep(50);
-                }
-                mVisualizer.setEnabled(false);
-            }
-            if (count == 0) {
-                return 0;
-            }
-            return energy/count;
-        }
-
-        public void release() {
-            if (mVisualizer != null) {
-                mVisualizer.release();
-                mVisualizer = null;
-            }
-        }
-    }
-
     private void getVirtualizer(int session) {
          if (mVirtualizer == null || session != mSession) {
              if (session != mSession && mVirtualizer != null) {
diff --git a/opengl/java/android/opengl/GLWallpaperService.java b/opengl/java/android/opengl/GLWallpaperService.java
new file mode 100644
index 0000000..c954fed
--- /dev/null
+++ b/opengl/java/android/opengl/GLWallpaperService.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.opengl;
+
+/**
+ * reserve this namespace for future use
+ * (making sure external developers don't use it)
+ * @hide
+ */
+class GLWallpaperService {
+}
diff --git a/packages/SystemUI/res/drawable-hdpi/notification_dragger.png b/packages/SystemUI/res/drawable-hdpi/notification_dragger.png
deleted file mode 100644
index 71b5507..0000000
--- a/packages/SystemUI/res/drawable-hdpi/notification_dragger.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_0.png b/packages/SystemUI/res/drawable-mdpi/battery_0.png
index 77162c8..e81638d 100644
--- a/packages/SystemUI/res/drawable-mdpi/battery_0.png
+++ b/packages/SystemUI/res/drawable-mdpi/battery_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_10.png b/packages/SystemUI/res/drawable-mdpi/battery_10.png
new file mode 100644
index 0000000..a739f50
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_10.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_100.png b/packages/SystemUI/res/drawable-mdpi/battery_100.png
index 588a74b..9a66db3 100644
--- a/packages/SystemUI/res/drawable-mdpi/battery_100.png
+++ b/packages/SystemUI/res/drawable-mdpi/battery_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_25.png b/packages/SystemUI/res/drawable-mdpi/battery_25.png
index b11570b..7ae7ebb 100644
--- a/packages/SystemUI/res/drawable-mdpi/battery_25.png
+++ b/packages/SystemUI/res/drawable-mdpi/battery_25.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_5.png b/packages/SystemUI/res/drawable-mdpi/battery_5.png
new file mode 100644
index 0000000..1a15a21
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_5.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_50.png b/packages/SystemUI/res/drawable-mdpi/battery_50.png
index 94d7eca..7088066 100644
--- a/packages/SystemUI/res/drawable-mdpi/battery_50.png
+++ b/packages/SystemUI/res/drawable-mdpi/battery_50.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_75.png b/packages/SystemUI/res/drawable-mdpi/battery_75.png
index 3cb3591..d4cfb6f 100644
--- a/packages/SystemUI/res/drawable-mdpi/battery_75.png
+++ b/packages/SystemUI/res/drawable-mdpi/battery_75.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/notification_dragger.png b/packages/SystemUI/res/drawable-mdpi/notification_dragger.png
deleted file mode 100644
index fad1f32..0000000
--- a/packages/SystemUI/res/drawable-mdpi/notification_dragger.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_0.png b/packages/SystemUI/res/drawable-mdpi/signal_0.png
index cf436ad..ff2727b 100644
--- a/packages/SystemUI/res/drawable-mdpi/signal_0.png
+++ b/packages/SystemUI/res/drawable-mdpi/signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_100.png b/packages/SystemUI/res/drawable-mdpi/signal_100.png
index 0011a1b..55c34ff 100644
--- a/packages/SystemUI/res/drawable-mdpi/signal_100.png
+++ b/packages/SystemUI/res/drawable-mdpi/signal_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_25.png b/packages/SystemUI/res/drawable-mdpi/signal_25.png
index 69c643f..a415596 100644
--- a/packages/SystemUI/res/drawable-mdpi/signal_25.png
+++ b/packages/SystemUI/res/drawable-mdpi/signal_25.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_50.png b/packages/SystemUI/res/drawable-mdpi/signal_50.png
index c8acd72..3c5a708 100644
--- a/packages/SystemUI/res/drawable-mdpi/signal_50.png
+++ b/packages/SystemUI/res/drawable-mdpi/signal_50.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_75.png b/packages/SystemUI/res/drawable-mdpi/signal_75.png
index 9650dfa..e176f37 100644
--- a/packages/SystemUI/res/drawable-mdpi/signal_75.png
+++ b/packages/SystemUI/res/drawable-mdpi/signal_75.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_scan1.png b/packages/SystemUI/res/drawable-mdpi/signal_scan1.png
new file mode 100644
index 0000000..f64b9cc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_scan1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_scan2.png b/packages/SystemUI/res/drawable-mdpi/signal_scan2.png
new file mode 100644
index 0000000..982feb4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_scan2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_scan3.png b/packages/SystemUI/res/drawable-mdpi/signal_scan3.png
new file mode 100644
index 0000000..a4c0689
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_scan3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_scan4.png b/packages/SystemUI/res/drawable-mdpi/signal_scan4.png
new file mode 100644
index 0000000..d1e95fe
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_scan4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png
index 5c57802..a2527b3 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png
index b2a94b6..ce64926 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png
index b2a94b6..c63c426 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png
index f219ded..9e64fe8 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_recent_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_recent_pressed.png
index 4dd8dc7..350a3e9 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_recent_pressed.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_recent_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_0.png b/packages/SystemUI/res/drawable-mdpi/wifi_0.png
new file mode 100644
index 0000000..e81638d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_100.png b/packages/SystemUI/res/drawable-mdpi/wifi_100.png
new file mode 100644
index 0000000..4468389
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_25.png b/packages/SystemUI/res/drawable-mdpi/wifi_25.png
new file mode 100644
index 0000000..0e389a7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_25.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_50.png b/packages/SystemUI/res/drawable-mdpi/wifi_50.png
new file mode 100644
index 0000000..1cad0d1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_50.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_75.png b/packages/SystemUI/res/drawable-mdpi/wifi_75.png
new file mode 100644
index 0000000..dd2d7dc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_75.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_scan1.png b/packages/SystemUI/res/drawable-mdpi/wifi_scan1.png
new file mode 100644
index 0000000..0e389a7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_scan1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_scan2.png b/packages/SystemUI/res/drawable-mdpi/wifi_scan2.png
new file mode 100644
index 0000000..34e7c70
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_scan2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_scan3.png b/packages/SystemUI/res/drawable-mdpi/wifi_scan3.png
new file mode 100644
index 0000000..4c2a9e1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_scan3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/wifi_scan4.png b/packages/SystemUI/res/drawable-mdpi/wifi_scan4.png
new file mode 100644
index 0000000..b504131
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/wifi_scan4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/battery.xml b/packages/SystemUI/res/drawable/battery.xml
index f4a720d..3bc0c74 100644
--- a/packages/SystemUI/res/drawable/battery.xml
+++ b/packages/SystemUI/res/drawable/battery.xml
@@ -20,12 +20,8 @@
 
 <level-list xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:maxLevel="1" android:drawable="@drawable/battery_0" />
-    <item android:maxLevel="5">
-        <animation-list android:oneshot="false">
-            <item android:drawable="@drawable/battery_0" android:duration="250" />
-            <item android:drawable="@drawable/battery_25" android:duration="250" />
-        </animation-list>
-    </item>
+    <item android:maxLevel="5" android:drawable="@drawable/battery_5" />
+    <item android:maxLevel="10" android:drawable="@drawable/battery_10" />
     <item android:maxLevel="25" android:drawable="@drawable/battery_25" />
     <item android:maxLevel="50" android:drawable="@drawable/battery_50" />
     <item android:maxLevel="75" android:drawable="@drawable/battery_75" />
diff --git a/packages/SystemUI/res/drawable/signal.xml b/packages/SystemUI/res/drawable/signal.xml
index 5aa211a..7367418 100644
--- a/packages/SystemUI/res/drawable/signal.xml
+++ b/packages/SystemUI/res/drawable/signal.xml
@@ -24,6 +24,5 @@
     <item android:maxLevel="50" android:drawable="@drawable/signal_50" />
     <item android:maxLevel="75" android:drawable="@drawable/signal_75" />
     <item android:maxLevel="101" android:drawable="@drawable/signal_100" />
-
 </level-list>
 
diff --git a/packages/SystemUI/res/drawable/signal_scan.xml b/packages/SystemUI/res/drawable/signal_scan.xml
new file mode 100644
index 0000000..fe21992
--- /dev/null
+++ b/packages/SystemUI/res/drawable/signal_scan.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/signal_scan1" android:duration="500" />
+    <item android:drawable="@drawable/signal_scan2" android:duration="500" />
+    <item android:drawable="@drawable/signal_scan3" android:duration="500" />
+    <item android:drawable="@drawable/signal_scan4" android:duration="500" />
+    <item android:drawable="@drawable/signal_scan3" android:duration="500" />
+    <item android:drawable="@drawable/signal_scan2" android:duration="500" />
+</animation-list>
+
diff --git a/packages/SystemUI/res/drawable/wifi.xml b/packages/SystemUI/res/drawable/wifi.xml
new file mode 100644
index 0000000..6e63af6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/wifi.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:maxLevel="01" android:drawable="@drawable/wifi_0" />
+    <item android:maxLevel="25" android:drawable="@drawable/wifi_25" />
+    <item android:maxLevel="50" android:drawable="@drawable/wifi_50" />
+    <item android:maxLevel="75" android:drawable="@drawable/wifi_75" />
+    <item android:maxLevel="101" android:drawable="@drawable/wifi_100" />
+
+</level-list>
+
diff --git a/packages/SystemUI/res/drawable/wifi_scan.xml b/packages/SystemUI/res/drawable/wifi_scan.xml
new file mode 100644
index 0000000..5888d0e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/wifi_scan.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/wifi_scan1" android:duration="500" />
+    <item android:drawable="@drawable/wifi_scan2" android:duration="500" />
+    <item android:drawable="@drawable/wifi_scan3" android:duration="500" />
+    <item android:drawable="@drawable/wifi_scan4" android:duration="500" />
+    <item android:drawable="@drawable/wifi_scan3" android:duration="500" />
+    <item android:drawable="@drawable/wifi_scan2" android:duration="500" />
+</animation-list>
+
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index ffb1571..481bcde 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -101,8 +101,8 @@
         </LinearLayout>
 
         <include layout="@layout/status_bar_center"
-            android:layout_width="100dip"
-            android:layout_height="wrap_content"
+            android:layout_width="160dip"
+            android:layout_height="match_parent"
             android:layout_centerInParent="true"
             />
 
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
index 775fea0..5bf8316 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
@@ -18,8 +18,8 @@
 <RelativeLayout android:id="@+id/systemInfo"
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
-    android:layout_width="100dip"
-    android:layout_height="wrap_content"
+    android:layout_width="160dip"
+    android:layout_height="match_parent"
     android:layout_centerInParent="true"
     android:clickable="true"
     android:onClick="systemInfoClicked"
@@ -27,8 +27,9 @@
     <com.android.systemui.statusbar.Clock
         style="@*android:style/TextAppearance.StatusBar.Icon"
         android:id="@+id/clock"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_width="64dip"
+        android:layout_height="48dip"
+        android:layout_centerInParent="true"
         android:singleLine="true"
         android:gravity="center"
         android:textSize="16sp"
@@ -37,18 +38,16 @@
         />
     <ImageView
         android:id="@+id/battery"
-        android:layout_width="50dip"
-        android:layout_height="wrap_content"
-        android:layout_alignParentLeft="true"
-        android:layout_below="@id/clock"
+        android:layout_width="48dip"
+        android:layout_height="48dip"
+        android:layout_toLeftOf="@id/clock"
         android:src="@drawable/battery"
         />
     <ImageView
         android:id="@+id/signal"
-        android:layout_width="50dip"
-        android:layout_height="wrap_content"
-        android:layout_alignParentRight="true"
-        android:layout_below="@id/clock"
+        android:layout_width="48dip"
+        android:layout_height="48dip"
+        android:layout_toRightOf="@id/clock"
         android:src="@drawable/signal"
         />
 </RelativeLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
index 273674f..36c6388 100644
--- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
@@ -92,8 +92,10 @@
             android:layout_alignParentLeft="true"
             >
             <ImageView android:id="@+id/battery_meter"
-                android:layout_width="wrap_content"
+                android:layout_width="96dip"
                 android:layout_height="wrap_content"
+                android:layout_centerHorizontal="true"
+                android:scaleType="centerCrop"
                 android:src="@drawable/battery"
                 />
 
@@ -129,8 +131,10 @@
             android:layout_alignParentRight="true"
             >
             <ImageView android:id="@+id/signal_meter"
-                android:layout_width="wrap_content"
+                android:layout_width="96dip"
                 android:layout_height="wrap_content"
+                android:layout_centerHorizontal="true"
+                android:scaleType="centerCrop"
                 android:src="@drawable/signal"
                 />
 
diff --git a/packages/SystemUI/res/values-xlarge/strings.xml b/packages/SystemUI/res/values-xlarge/strings.xml
index 4aa4b47..3c59c92 100644
--- a/packages/SystemUI/res/values-xlarge/strings.xml
+++ b/packages/SystemUI/res/values-xlarge/strings.xml
@@ -20,4 +20,58 @@
     <!-- The text for the button in the notification window-shade that clears
          all of the currently visible notifications. -->
     <string name="status_bar_clear_all_button">Clear all</string>
+
+    <!-- System panel ("Quick Settings") -->
+
+    <!-- Text to display underneath the graphical battery meter. Should
+         include the word for "battery" and a place for the percentage charge
+         available. [CHAR LIMIT=20] -->
+    <string name="system_panel_battery_meter_format">
+        Battery: <xliff:g id="number">%d</xliff:g><xliff:g id="percent">%%</xliff:g>
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         no connection is available. [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_disconnected">
+        no internet connection
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying information about a connected, named Wi-Fi network.
+         Should include the word for "Wi-Fi" and a placeholder for the
+         wireless network's SSID. [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_wifi_ssid_format">
+        Wi-Fi: <xliff:g id="ssid">%s</xliff:g>
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying Wi-Fi status and Wi-Fi is connected to a network
+         whose SSID is not available.
+         [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_wifi_nossid">
+        Wi-Fi: connected
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying Wi-Fi status and Wi-Fi is in the process of
+         connecting to a network.  [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_wifi_connecting">
+        Wi-Fi: connecting…
+    </string>
+ 
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying mobile data (3G) status and a network connection is
+         available.
+         [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_data_connected">
+        Mobile data: connected
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying mobile data (3G) status and a network connection is
+         unavailable.
+         [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_data_connecting">
+        Mobile data: connecting…
+    </string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
index a03393b..dfa2d10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
@@ -45,6 +45,7 @@
 import android.provider.Settings;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Slog;
@@ -66,11 +67,18 @@
 
 import java.util.List;
 
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.cdma.EriInfo;
+import com.android.internal.telephony.cdma.TtyIntent;
+
 import com.android.systemui.statusbar.*;
 import com.android.systemui.R;
 
 public class SystemPanel extends LinearLayout {
     private static final String TAG = "SystemPanel";
+    private static final boolean DEBUG = TabletStatusBarService.DEBUG;
+    private static final boolean DEBUG_SIGNAL = true;
 
     private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 5;
     private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
@@ -93,7 +101,22 @@
 
     private final AudioManager mAudioManager;
     private final WifiManager mWifiManager;
+    private final TelephonyManager mPhone;
 
+    // state trackers for telephony code
+    IccCard.State mSimState = IccCard.State.READY;
+    int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
+    int mDataState = TelephonyManager.DATA_DISCONNECTED;
+    ServiceState mServiceState;
+    SignalStrength mSignalStrength;
+
+    // state for the meters
+    boolean mWifiEnabled, mWifiConnected;
+    int mWifiLevel;
+    String mWifiSsid;
+
+    boolean mDataEnabled, mDataConnected, mDataRoaming;
+    int mDataLevel;
 
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -107,18 +130,154 @@
                     || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)
                     || action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)
                     || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-                updateWifi(intent);
+                updateWifiState(intent);
+            } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+                updateSimState(intent);
             }
         }
     };
 
-    boolean mWifiEnabled, mWifiConnected;
-    int mWifiLevel;
-    String mWifiSsid;
+    private final void updateSimState(Intent intent) {
+        String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
+        if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
+            mSimState = IccCard.State.ABSENT;
+        }
+        else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
+            mSimState = IccCard.State.READY;
+        }
+        else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+            final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
+            if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
+                mSimState = IccCard.State.PIN_REQUIRED;
+            }
+            else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
+                mSimState = IccCard.State.PUK_REQUIRED;
+            }
+            else {
+                mSimState = IccCard.State.NETWORK_LOCKED;
+            }
+        } else {
+            mSimState = IccCard.State.UNKNOWN;
+        }
+        updateDataState();
+    }
 
-    private void updateWifi(Intent intent) {
-        if (TabletStatusBarService.DEBUG)
-            Slog.d(TabletStatusBarService.TAG, "updateWifi: " + intent);
+    private boolean isCdma() {
+        return (mSignalStrength != null) && !mSignalStrength.isGsm();
+    }
+
+    private boolean isEvdo() {
+        return ( (mServiceState != null)
+                 && ((mServiceState.getRadioTechnology()
+                        == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
+                     || (mServiceState.getRadioTechnology()
+                        == ServiceState.RADIO_TECHNOLOGY_EVDO_A)
+                     || (mServiceState.getRadioTechnology()
+                        == ServiceState.RADIO_TECHNOLOGY_EVDO_B)));
+    }
+
+    private boolean hasService() {
+        if (mServiceState != null) {
+            switch (mServiceState.getState()) {
+                case ServiceState.STATE_OUT_OF_SERVICE:
+                case ServiceState.STATE_POWER_OFF:
+                    return false;
+                default:
+                    return true;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    private int getCdmaLevel() {
+        if (mSignalStrength == null) return 0;
+        final int cdmaDbm = mSignalStrength.getCdmaDbm();
+        final int cdmaEcio = mSignalStrength.getCdmaEcio();
+        int levelDbm = 0;
+        int levelEcio = 0;
+
+        if (cdmaDbm >= -75) levelDbm = 4;
+        else if (cdmaDbm >= -85) levelDbm = 3;
+        else if (cdmaDbm >= -95) levelDbm = 2;
+        else if (cdmaDbm >= -100) levelDbm = 1;
+        else levelDbm = 0;
+
+        // Ec/Io are in dB*10
+        if (cdmaEcio >= -90) levelEcio = 4;
+        else if (cdmaEcio >= -110) levelEcio = 3;
+        else if (cdmaEcio >= -130) levelEcio = 2;
+        else if (cdmaEcio >= -150) levelEcio = 1;
+        else levelEcio = 0;
+
+        return (levelDbm < levelEcio) ? levelDbm : levelEcio;
+    }
+
+    private int getEvdoLevel() {
+        if (mSignalStrength == null) return 0;
+        int evdoDbm = mSignalStrength.getEvdoDbm();
+        int evdoSnr = mSignalStrength.getEvdoSnr();
+        int levelEvdoDbm = 0;
+        int levelEvdoSnr = 0;
+
+        if (evdoDbm >= -65) levelEvdoDbm = 4;
+        else if (evdoDbm >= -75) levelEvdoDbm = 3;
+        else if (evdoDbm >= -90) levelEvdoDbm = 2;
+        else if (evdoDbm >= -105) levelEvdoDbm = 1;
+        else levelEvdoDbm = 0;
+
+        if (evdoSnr >= 7) levelEvdoSnr = 4;
+        else if (evdoSnr >= 5) levelEvdoSnr = 3;
+        else if (evdoSnr >= 3) levelEvdoSnr = 2;
+        else if (evdoSnr >= 1) levelEvdoSnr = 1;
+        else levelEvdoSnr = 0;
+
+        return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+    }
+
+    private void updateDataState() {
+        mDataConnected = hasService() && (mDataState == TelephonyManager.DATA_CONNECTED);
+
+        if (isCdma()) {
+            // these functions return a value from 0 to 4, inclusive
+            if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){
+                mDataLevel = getEvdoLevel() * 25;
+            } else {
+                mDataLevel = getCdmaLevel() * 25;
+            }
+        } else {
+            // GSM
+            
+            int asu = (mSignalStrength == null) ? 0 : mSignalStrength.getGsmSignalStrength();
+
+            // asu on [0,31]; 99 = unknown
+            // Android has historically shown anything >=12 as "full"
+            // XXX: tune this based on Industry Best Practices(TM)
+            if (asu <= 2 || asu == 99) mDataLevel = 0;
+            else mDataLevel = (int)(((float)Math.max(asu, 15) / 15) * 100);
+
+            mDataRoaming = mPhone.isNetworkRoaming();
+
+            mDataConnected = mDataConnected
+                && (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN);
+        }
+
+        if (DEBUG_SIGNAL || DEBUG) {
+            Slog.d(TAG, "updateDataState: connected=" + mDataConnected 
+                    + " level=" + mDataLevel
+                    + " isEvdo=" + isEvdo()
+                    + " isCdma=" + isCdma()
+                    + " mPhoneState=" + mPhoneState
+                    + " mDataState=" + mDataState
+                    );
+        }
+
+        refreshSignalMeters();
+    }
+
+    private void updateWifiState(Intent intent) {
+        if (DEBUG)
+            Slog.d(TAG, "updateWifiState: " + intent);
 
         final String action = intent.getAction();
         final boolean wasConnected = mWifiConnected;
@@ -142,8 +301,8 @@
 
         if (mWifiConnected && !wasConnected) {
             WifiInfo info = mWifiManager.getConnectionInfo();
-            if (TabletStatusBarService.DEBUG)
-                Slog.d(TabletStatusBarService.TAG, "updateWifi: just connected: info=" + info);
+            if (DEBUG)
+                Slog.d(TAG, "updateWifiState: just connected: info=" + info);
 
             if (info != null) {
                 // grab the initial signal strength
@@ -164,22 +323,40 @@
             }
         }
 
-        if (!mWifiEnabled) {
-            mWifiSsid = "disabled";
-            mWifiLevel = 0;
-        } else if (!mWifiConnected) {
-            mWifiSsid = "disconnected";
-            mWifiLevel = 0;
-        } else if (mWifiSsid == null) {
-            mWifiSsid = "unknown";
+        refreshSignalMeters();
+    }
+
+    // figure out what to show: first wifi, then 3G, then nothing
+    void refreshSignalMeters() {
+        if (mSignalMeter == null) return; // no UI yet
+
+        Context ctxt = getContext();
+
+        String text = null;
+        int level = 0;
+
+        if (mWifiConnected) {
+            if (mWifiSsid == null) {
+                text = ctxt.getString(R.string.system_panel_signal_meter_wifi_nossid);
+            } else {
+                text = ctxt.getString(R.string.system_panel_signal_meter_wifi_ssid_format,
+                                      mWifiSsid);
+            }
+            level = mWifiLevel;
+        } else if (mDataConnected) {
+            text = ctxt.getString(R.string.system_panel_signal_meter_data_connected);
+            level = mDataLevel;
+        } else {
+            text = ctxt.getString(R.string.system_panel_signal_meter_disconnected);
+            level = 0;
         }
 
-        mSignalMeter.setImageResource(R.drawable.signal);
-        mSignalMeter.setImageLevel(mWifiLevel);
-        mSignalText.setText(String.format("Wi-Fi: %s", mWifiSsid)); // XXX: localize
+        mSignalMeter.setImageResource(mWifiConnected ? R.drawable.wifi : R.drawable.signal);
+        mSignalMeter.setImageLevel(level);
+        mSignalText.setText(text);
 
         // hack for now
-        mBar.setWifiMeter(mWifiLevel);
+        mBar.setSignalMeter(level, mWifiConnected);
     }
 
     public void setBar(TabletStatusBarService bar) {
@@ -192,7 +369,8 @@
 
         mBatteryMeter.setImageResource(plugged ? R.drawable.battery_charging : R.drawable.battery);
         mBatteryMeter.setImageLevel(level);
-        mBatteryText.setText(String.format("Battery: %d%%", level));
+        mBatteryText.setText(getContext()
+                .getString(R.string.system_panel_battery_meter_format, level));
 
         // hack for now
         mBar.setBatteryMeter(level, plugged);
@@ -208,13 +386,21 @@
         // get notified of phone state changes
         TelephonyManager telephonyManager =
                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
-        telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+        telephonyManager.listen(mPhoneStateListener,
+                          PhoneStateListener.LISTEN_SERVICE_STATE
+                        | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
+                        | PhoneStateListener.LISTEN_CALL_STATE
+                        | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
+                        | PhoneStateListener.LISTEN_DATA_ACTIVITY);
 
         // wifi status info
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
         
-        // audio status notifications
+        // audio status 
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+
+        // mobile data 
+        mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
     }
 
     public void onAttachedToWindow() {
@@ -259,6 +445,7 @@
             }
         });
 
+        // register for broadcasts
         IntentFilter filter = new IntentFilter();
         filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
@@ -266,8 +453,9 @@
         filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         getContext().registerReceiver(mReceiver, filter);
-
+        
         mBatteryMeter = (ImageView)findViewById(R.id.battery_meter);
         mBatteryMeter.setImageResource(R.drawable.battery);
         mBatteryMeter.setImageLevel(0);
@@ -277,6 +465,8 @@
 
         mBatteryText = (TextView)findViewById(R.id.battery_info);
         mSignalText = (TextView)findViewById(R.id.signal_info);
+
+        refreshSignalMeters();
     }
 
     public void onDetachedFromWindow() {
@@ -362,11 +552,42 @@
     PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
         @Override
         public void onServiceStateChanged(ServiceState serviceState) {
-            Slog.d(TAG, "phone service state changed: " + serviceState.getState());
+            if (DEBUG_SIGNAL || DEBUG) {
+                Slog.d(TAG, "phone service state changed: " + serviceState.getState());
+            }
+            mServiceState = serviceState;
             mAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
             if (mAirplaneButton != null) {
                 mAirplaneButton.setAlpha(mAirplaneMode ? 0xFF : 0x7F);
             }
+            updateDataState();
+        }
+        @Override
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            if (DEBUG_SIGNAL || DEBUG) {
+                Slog.d(TAG, "onSignalStrengthsChanged: " + signalStrength);
+            }
+            mSignalStrength = signalStrength;
+            updateDataState();
+        }
+        @Override
+        public void onCallStateChanged(int state, String incomingNumber) {
+            mPhoneState = state;
+            // In cdma, if a voice call is made, RSSI should switch to 1x.
+            if (isCdma()) {
+                updateDataState();
+            }
+        }
+
+        @Override
+        public void onDataConnectionStateChanged(int state, int networkType) {
+            if (DEBUG_SIGNAL || DEBUG) {
+                Slog.d(TAG, "onDataConnectionStateChanged: state=" + state 
+                        + " type=" + networkType);
+            }
+            mDataState = state;
+//            updateDataNetType(networkType);
+            updateDataState();
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
index 18b9b41..087671a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
@@ -212,10 +212,15 @@
         mBatteryMeter.setImageLevel(level);
     }
 
-    public void setWifiMeter(int level) {
-        if (DEBUG) Slog.d(TAG, "wifi=" + level);
-        mSignalMeter.setImageResource(R.drawable.signal);
-        mSignalMeter.setImageLevel(level);
+    public void setSignalMeter(int level, boolean isWifi) {
+        if (DEBUG) Slog.d(TAG, "signal=" + level);
+        if (level < 0) {
+            mSignalMeter.setImageResource(isWifi ? R.drawable.wifi_scan : R.drawable.signal_scan);
+            mSignalMeter.setImageLevel(0);
+        } else {
+            mSignalMeter.setImageResource(isWifi ? R.drawable.wifi : R.drawable.signal);
+            mSignalMeter.setImageLevel(level);
+        }
     }
 
     public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index d5750c6..4407e96 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -4798,6 +4798,8 @@
         Parcel data = null;
         Parcel reply = null;
 
+        BufferedWriter out = null;
+
         // Any uncaught exception will crash the system process
         try {
             // Find the hashcode of the window
@@ -4835,6 +4837,12 @@
 
             reply.readException();
 
+            if (!client.isOutputShutdown()) {
+                out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
+                out.write("DONE\n");
+                out.flush();
+            }
+
         } catch (Exception e) {
             Slog.w(TAG, "Could not send command " + command + " with parameters " + parameters, e);
             success = false;
@@ -4845,6 +4853,13 @@
             if (reply != null) {
                 reply.recycle();
             }
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException e) {
+
+                }
+            }
         }
 
         return success;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 56159b5..123dc3e 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -610,8 +610,8 @@
      * All currently bound service connections.  Keys are the IBinder of
      * the client's IServiceConnection.
      */
-    final HashMap<IBinder, ConnectionRecord> mServiceConnections
-            = new HashMap<IBinder, ConnectionRecord>();
+    final HashMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections
+            = new HashMap<IBinder, ArrayList<ConnectionRecord>>();
 
     /**
      * List of services that we have been asked to start,
@@ -4301,12 +4301,10 @@
                         + " when granting permission to uri " + uri);
             }
             if (targetPkg == null) {
-                Slog.w(TAG, "grantUriPermission: null target");
-                return;
+                throw new IllegalArgumentException("null target");
             }
             if (uri == null) {
-                Slog.w(TAG, "grantUriPermission: null uri");
-                return;
+                throw new IllegalArgumentException("null uri");
             }
 
             grantUriPermissionLocked(r.info.uid, targetPkg, uri, modeFlags,
@@ -4458,6 +4456,56 @@
         }
     }
 
+    @Override
+    public IBinder newUriPermissionOwner(String name) {
+        synchronized(this) {
+            UriPermissionOwner owner = new UriPermissionOwner(this, name);
+            return owner.getExternalTokenLocked();
+        }
+    }
+
+    @Override
+    public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg,
+            Uri uri, int modeFlags) {
+        synchronized(this) {
+            UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
+            if (owner == null) {
+                throw new IllegalArgumentException("Unknown owner: " + token);
+            }
+            if (fromUid != Binder.getCallingUid()) {
+                if (Binder.getCallingUid() != Process.myUid()) {
+                    // Only system code can grant URI permissions on behalf
+                    // of other users.
+                    throw new SecurityException("nice try");
+                }
+            }
+            if (targetPkg == null) {
+                throw new IllegalArgumentException("null target");
+            }
+            if (uri == null) {
+                throw new IllegalArgumentException("null uri");
+            }
+
+            grantUriPermissionLocked(fromUid, targetPkg, uri, modeFlags, owner);
+        }
+    }
+
+    @Override
+    public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode) {
+        synchronized(this) {
+            UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
+            if (owner == null) {
+                throw new IllegalArgumentException("Unknown owner: " + token);
+            }
+
+            if (uri == null) {
+                owner.removeUriPermissionsLocked(mode);
+            } else {
+                owner.removeUriPermissionLocked(uri, mode);
+            }
+        }
+    }
+
     public void showWaitingForDebugger(IApplicationThread who, boolean waiting) {
         synchronized (this) {
             ProcessRecord app =
@@ -7404,12 +7452,14 @@
             if (mServiceConnections.size() > 0) {
                 if (needSep) pw.println(" ");
                 pw.println("  Connection bindings to services:");
-                Iterator<ConnectionRecord> it
+                Iterator<ArrayList<ConnectionRecord>> it
                         = mServiceConnections.values().iterator();
                 while (it.hasNext()) {
-                    ConnectionRecord r = it.next();
-                    pw.print("  * "); pw.println(r);
-                    r.dump(pw, "    ");
+                    ArrayList<ConnectionRecord> r = it.next();
+                    for (int i=0; i<r.size(); i++) {
+                        pw.print("  * "); pw.println(r.get(i));
+                        r.get(i).dump(pw, "    ");
+                    }
                 }
                 needSep = true;
             }
@@ -7687,18 +7737,21 @@
                 while (it.hasNext()) {
                     ServiceRecord r = it.next();
                     if (r.connections.size() > 0) {
-                        Iterator<ConnectionRecord> jt
+                        Iterator<ArrayList<ConnectionRecord>> jt
                                 = r.connections.values().iterator();
                         while (jt.hasNext()) {
-                            ConnectionRecord c = jt.next();
-                            if (c.binding.client != app) {
-                                try {
-                                    //c.conn.connected(r.className, null);
-                                } catch (Exception e) {
-                                    // todo: this should be asynchronous!
-                                    Slog.w(TAG, "Exception thrown disconnected servce "
-                                          + r.shortName
-                                          + " from app " + app.processName, e);
+                            ArrayList<ConnectionRecord> cl = jt.next();
+                            for (int i=0; i<cl.size(); i++) {
+                                ConnectionRecord c = cl.get(i);
+                                if (c.binding.client != app) {
+                                    try {
+                                        //c.conn.connected(r.className, null);
+                                    } catch (Exception e) {
+                                        // todo: this should be asynchronous!
+                                        Slog.w(TAG, "Exception thrown disconnected servce "
+                                              + r.shortName
+                                              + " from app " + app.processName, e);
+                                    }
                                 }
                             }
                         }
@@ -7728,7 +7781,9 @@
                 }
                 sr.app = null;
                 sr.executeNesting = 0;
-                mStoppingServices.remove(sr);
+                if (mStoppingServices.remove(sr)) {
+                    if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
+                }
                 
                 boolean hasClients = sr.bindings.size() > 0;
                 if (hasClients) {
@@ -7781,6 +7836,7 @@
             ServiceRecord sr = mStoppingServices.get(i);
             if (sr.app == app) {
                 mStoppingServices.remove(i);
+                if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
             }
         }
         
@@ -8044,11 +8100,15 @@
         if (r.app != null && r.app.persistent) {
             info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS;
         }
-        for (ConnectionRecord conn : r.connections.values()) {
-            if (conn.clientLabel != 0) {
-                info.clientPackage = conn.binding.client.info.packageName;
-                info.clientLabel = conn.clientLabel;
-                break;
+
+        for (ArrayList<ConnectionRecord> connl : r.connections.values()) {
+            for (int i=0; i<connl.size(); i++) {
+                ConnectionRecord conn = connl.get(i);
+                if (conn.clientLabel != 0) {
+                    info.clientPackage = conn.binding.client.info.packageName;
+                    info.clientLabel = conn.clientLabel;
+                    return info;
+                }
             }
         }
         return info;
@@ -8083,9 +8143,11 @@
         synchronized (this) {
             ServiceRecord r = mServices.get(name);
             if (r != null) {
-                for (ConnectionRecord conn : r.connections.values()) {
-                    if (conn.clientIntent != null) {
-                        return conn.clientIntent;
+                for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
+                    for (int i=0; i<conn.size(); i++) {
+                        if (conn.get(i).clientIntent != null) {
+                            return conn.get(i).clientIntent;
+                        }
                     }
                 }
             }
@@ -8262,8 +8324,8 @@
         while (r.pendingStarts.size() > 0) {
             try {
                 ServiceRecord.StartItem si = r.pendingStarts.remove(0);
-                if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to service: "
-                        + r.name + " " + r.intent + " args=" + si.intent);
+                if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to: "
+                        + r + " " + r.intent + " args=" + si.intent);
                 if (si.intent == null) {
                     // If somehow we got a dummy start at the front, then
                     // just drop it here.
@@ -8274,8 +8336,9 @@
                 si.deliveryCount++;
                 if (si.targetPermissionUid >= 0) {
                     grantUriPermissionUncheckedFromIntentLocked(si.targetPermissionUid,
-                            r.packageName, si.intent, si);
+                            r.packageName, si.intent, si.getUriPermissionsLocked());
                 }
+                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING start of " + r);
                 bumpServiceExecutingLocked(r);
                 if (!oomAdjusted) {
                     oomAdjusted = true;
@@ -8292,6 +8355,7 @@
             } catch (RemoteException e) {
                 // Remote process gone...  we'll let the normal cleanup take
                 // care of this.
+                if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while scheduling start: " + r);
                 break;
             } catch (Exception e) {
                 Slog.w(TAG, "Unexpected exception", e);
@@ -8308,9 +8372,9 @@
         }
         if ((!i.requested || rebind) && i.apps.size() > 0) {
             try {
+                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bind of " + r
+                        + " in " + i + ": shouldUnbind=" + i.hasBound);
                 bumpServiceExecutingLocked(r);
-                if (DEBUG_SERVICE) Slog.v(TAG, "Connecting binding " + i
-                        + ": shouldUnbind=" + i.hasBound);
                 r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
                 if (!rebind) {
                     i.requested = true;
@@ -8318,6 +8382,7 @@
                 i.hasBound = true;
                 i.doRebind = false;
             } catch (RemoteException e) {
+                if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while binding " + r);
                 return false;
             }
         }
@@ -8344,13 +8409,12 @@
         r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
 
         app.services.add(r);
+        if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING create of " + r + " " + r.intent);
         bumpServiceExecutingLocked(r);
         updateLruProcessLocked(app, true, true);
 
         boolean created = false;
         try {
-            if (DEBUG_SERVICE) Slog.v(TAG, "Scheduling start service: "
-                    + r.name + " " + r.intent);
             mStringBuilder.setLength(0);
             r.intent.getIntent().toShortString(mStringBuilder, false, true);
             EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE,
@@ -8510,8 +8574,7 @@
             return true;
         }
 
-        if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up service " + r.name
-                + " " + r.intent);
+        if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent);
 
         // We are now bringing the service up, so no longer in the
         // restarting state.
@@ -8562,27 +8625,30 @@
             if (!force) {
                 // XXX should probably keep a count of the number of auto-create
                 // connections directly in the service.
-                Iterator<ConnectionRecord> it = r.connections.values().iterator();
+                Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
                 while (it.hasNext()) {
-                    ConnectionRecord cr = it.next();
-                    if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
-                        return;
+                    ArrayList<ConnectionRecord> cr = it.next();
+                    for (int i=0; i<cr.size(); i++) {
+                        if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
+                            return;
+                        }
                     }
                 }
             }
 
             // Report to all of the connections that the service is no longer
             // available.
-            Iterator<ConnectionRecord> it = r.connections.values().iterator();
+            Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();
             while (it.hasNext()) {
-                ConnectionRecord c = it.next();
-                try {
-                    // todo: shouldn't be a synchronous call!
-                    c.conn.connected(r.name, null);
-                } catch (Exception e) {
-                    Slog.w(TAG, "Failure disconnecting service " + r.name +
-                          " to connection " + c.conn.asBinder() +
-                          " (in " + c.binding.client.processName + ")", e);
+                ArrayList<ConnectionRecord> c = it.next();
+                for (int i=0; i<c.size(); i++) {
+                    try {
+                        c.get(i).conn.connected(r.name, null);
+                    } catch (Exception e) {
+                        Slog.w(TAG, "Failure disconnecting service " + r.name +
+                              " to connection " + c.get(i).conn.asBinder() +
+                              " (in " + c.get(i).binding.client.processName + ")", e);
+                    }
                 }
             }
         }
@@ -8596,6 +8662,8 @@
                         + ": hasBound=" + ibr.hasBound);
                 if (r.app != null && r.app.thread != null && ibr.hasBound) {
                     try {
+                        if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bring down unbind of " + r
+                                + " for " + ibr);
                         bumpServiceExecutingLocked(r);
                         updateOomAdjLocked(r.app);
                         ibr.hasBound = false;
@@ -8610,15 +8678,13 @@
             }
         }
 
-        if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down service " + r.name
-                 + " " + r.intent);
+        if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent);
         EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE,
                 System.identityHashCode(r), r.shortName,
                 (r.app != null) ? r.app.pid : -1);
 
         mServices.remove(r.name);
         mServicesByIntent.remove(r.intent);
-        if (localLOGV) Slog.v(TAG, "BRING DOWN SERVICE: " + r.shortName);
         r.totalRestartCount = 0;
         unscheduleServiceRestartLocked(r);
 
@@ -8627,8 +8693,7 @@
         for (int i=0; i<N; i++) {
             if (mPendingServices.get(i) == r) {
                 mPendingServices.remove(i);
-                if (DEBUG_SERVICE) Slog.v(
-                    TAG, "Removed pending service: " + r.shortName);
+                if (DEBUG_SERVICE) Slog.v(TAG, "Removed pending: " + r);
                 i--;
                 N--;
             }
@@ -8650,8 +8715,11 @@
             r.app.services.remove(r);
             if (r.app.thread != null) {
                 try {
-                    if (DEBUG_SERVICE) Slog.v(TAG,
-                            "Stopping service: " + r.shortName);
+                    if (DEBUG_SERVICE) {
+                        RuntimeException here = new RuntimeException();
+                        here.fillInStackTrace();
+                        Slog.v(TAG, ">>> EXECUTING stop of " + r, here);
+                    }
                     bumpServiceExecutingLocked(r);
                     mStoppingServices.add(r);
                     updateOomAdjLocked(r.app);
@@ -8664,11 +8732,11 @@
                 updateServiceForegroundLocked(r.app, false);
             } else {
                 if (DEBUG_SERVICE) Slog.v(
-                    TAG, "Removed service that has no process: " + r.shortName);
+                    TAG, "Removed service that has no process: " + r);
             }
         } else {
             if (DEBUG_SERVICE) Slog.v(
-                TAG, "Removed service that is not running: " + r.shortName);
+                TAG, "Removed service that is not running: " + r);
         }
     }
 
@@ -8703,8 +8771,7 @@
             int targetPermissionUid = checkGrantUriPermissionFromIntentLocked(
                     callingUid, r.packageName, service);
             if (unscheduleServiceRestartLocked(r)) {
-                if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: "
-                        + r.shortName);
+                if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
             }
             r.startRequested = true;
             r.callStart = false;
@@ -8998,7 +9065,7 @@
 
             if (unscheduleServiceRestartLocked(s)) {
                 if (DEBUG_SERVICE) Slog.v(TAG, "BIND SERVICE WHILE RESTART PENDING: "
-                        + s.shortName);
+                        + s);
             }
 
             AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
@@ -9006,7 +9073,12 @@
                     connection, flags, clientLabel, clientIntent);
 
             IBinder binder = connection.asBinder();
-            s.connections.put(binder, c);
+            ArrayList<ConnectionRecord> clist = s.connections.get(binder);
+            if (clist == null) {
+                clist = new ArrayList<ConnectionRecord>();
+                s.connections.put(binder, clist);
+            }
+            clist.add(c);
             b.connections.add(c);
             if (activity != null) {
                 if (activity.connections == null) {
@@ -9015,7 +9087,12 @@
                 activity.connections.add(c);
             }
             b.client.connections.add(c);
-            mServiceConnections.put(binder, c);
+            clist = mServiceConnections.get(binder);
+            if (clist == null) {
+                clist = new ArrayList<ConnectionRecord>();
+                mServiceConnections.put(binder, clist);
+            }
+            clist.add(c);
 
             if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                 s.lastActivity = SystemClock.uptimeMillis();
@@ -9066,7 +9143,13 @@
         IBinder binder = c.conn.asBinder();
         AppBindRecord b = c.binding;
         ServiceRecord s = b.service;
-        s.connections.remove(binder);
+        ArrayList<ConnectionRecord> clist = s.connections.get(binder);
+        if (clist != null) {
+            clist.remove(c);
+            if (clist.size() == 0) {
+                s.connections.remove(binder);
+            }
+        }
         b.connections.remove(c);
         if (c.activity != null && c.activity != skipAct) {
             if (c.activity.connections != null) {
@@ -9076,7 +9159,13 @@
         if (b.client != skipApp) {
             b.client.connections.remove(c);
         }
-        mServiceConnections.remove(binder);
+        clist = mServiceConnections.get(binder);
+        if (clist != null) {
+            clist.remove(c);
+            if (clist.size() == 0) {
+                mServiceConnections.remove(binder);
+            }
+        }
 
         if (b.connections.size() == 0) {
             b.intent.apps.remove(b.client);
@@ -9087,6 +9176,8 @@
         if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
                 && b.intent.hasBound) {
             try {
+                if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING unbind of " + s
+                        + " from " + b);
                 bumpServiceExecutingLocked(s);
                 updateOomAdjLocked(s.app);
                 b.intent.hasBound = false;
@@ -9109,8 +9200,8 @@
         synchronized (this) {
             IBinder binder = connection.asBinder();
             if (DEBUG_SERVICE) Slog.v(TAG, "unbindService: conn=" + binder);
-            ConnectionRecord r = mServiceConnections.get(binder);
-            if (r == null) {
+            ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
+            if (clist == null) {
                 Slog.w(TAG, "Unbind failed: could not find connection for "
                       + connection.asBinder());
                 return false;
@@ -9118,11 +9209,14 @@
 
             final long origId = Binder.clearCallingIdentity();
 
-            removeConnectionLocked(r, null, null);
+            while (clist.size() > 0) {
+                ConnectionRecord r = clist.get(0);
+                removeConnectionLocked(r, null, null);
 
-            if (r.binding.service.app != null) {
-                // This could have made the service less important.
-                updateOomAdjLocked(r.binding.service.app);
+                if (r.binding.service.app != null) {
+                    // This could have made the service less important.
+                    updateOomAdjLocked(r.binding.service.app);
+                }
             }
 
             Binder.restoreCallingIdentity(origId);
@@ -9145,7 +9239,7 @@
 
             final long origId = Binder.clearCallingIdentity();
 
-            if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING SERVICE " + r.name
+            if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING " + r
                     + " " + intent + ": " + service);
             if (r != null) {
                 Intent.FilterComparison filter
@@ -9156,26 +9250,29 @@
                     b.requested = true;
                     b.received = true;
                     if (r.connections.size() > 0) {
-                        Iterator<ConnectionRecord> it
+                        Iterator<ArrayList<ConnectionRecord>> it
                                 = r.connections.values().iterator();
                         while (it.hasNext()) {
-                            ConnectionRecord c = it.next();
-                            if (!filter.equals(c.binding.intent.intent)) {
-                                if (DEBUG_SERVICE) Slog.v(
-                                        TAG, "Not publishing to: " + c);
-                                if (DEBUG_SERVICE) Slog.v(
-                                        TAG, "Bound intent: " + c.binding.intent.intent);
-                                if (DEBUG_SERVICE) Slog.v(
-                                        TAG, "Published intent: " + intent);
-                                continue;
-                            }
-                            if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
-                            try {
-                                c.conn.connected(r.name, service);
-                            } catch (Exception e) {
-                                Slog.w(TAG, "Failure sending service " + r.name +
-                                      " to connection " + c.conn.asBinder() +
-                                      " (in " + c.binding.client.processName + ")", e);
+                            ArrayList<ConnectionRecord> clist = it.next();
+                            for (int i=0; i<clist.size(); i++) {
+                                ConnectionRecord c = clist.get(i);
+                                if (!filter.equals(c.binding.intent.intent)) {
+                                    if (DEBUG_SERVICE) Slog.v(
+                                            TAG, "Not publishing to: " + c);
+                                    if (DEBUG_SERVICE) Slog.v(
+                                            TAG, "Bound intent: " + c.binding.intent.intent);
+                                    if (DEBUG_SERVICE) Slog.v(
+                                            TAG, "Published intent: " + intent);
+                                    continue;
+                                }
+                                if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c);
+                                try {
+                                    c.conn.connected(r.name, service);
+                                } catch (Exception e) {
+                                    Slog.w(TAG, "Failure sending service " + r.name +
+                                          " to connection " + c.conn.asBinder() +
+                                          " (in " + c.binding.client.processName + ")", e);
+                                }
                             }
                         }
                     }
@@ -9236,9 +9333,6 @@
             ServiceRecord r = (ServiceRecord)token;
             boolean inStopping = mStoppingServices.contains(token);
             if (r != null) {
-                if (DEBUG_SERVICE) Slog.v(TAG, "DONE EXECUTING SERVICE " + r.name
-                        + ": nesting=" + r.executeNesting
-                        + ", inStopping=" + inStopping);
                 if (r != token) {
                     Slog.w(TAG, "Done executing service " + r.name
                           + " with incorrect token: given " + token
@@ -9295,13 +9389,16 @@
                 serviceDoneExecutingLocked(r, inStopping);
                 Binder.restoreCallingIdentity(origId);
             } else {
-                Slog.w(TAG, "Done executing unknown service " + r.name
-                        + " with token " + token);
+                Slog.w(TAG, "Done executing unknown service from pid "
+                        + Binder.getCallingPid());
             }
         }
     }
 
     public void serviceDoneExecutingLocked(ServiceRecord r, boolean inStopping) {
+        if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r
+                + ": nesting=" + r.executeNesting
+                + ", inStopping=" + inStopping + ", app=" + r.app);
         r.executeNesting--;
         if (r.executeNesting <= 0 && r.app != null) {
             r.app.executingServices.remove(r);
@@ -9309,6 +9406,7 @@
                 mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r.app);
             }
             if (inStopping) {
+                if (DEBUG_SERVICE) Slog.v(TAG, "doneExecuting remove stopping " + r);
                 mStoppingServices.remove(r);
             }
             updateOomAdjLocked(r.app);
@@ -11099,61 +11197,64 @@
                 }
                 if (s.connections.size() > 0 && (adj > FOREGROUND_APP_ADJ
                         || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
-                    Iterator<ConnectionRecord> kt
+                    Iterator<ArrayList<ConnectionRecord>> kt
                             = s.connections.values().iterator();
                     while (kt.hasNext() && adj > FOREGROUND_APP_ADJ) {
-                        // XXX should compute this based on the max of
-                        // all connected clients.
-                        ConnectionRecord cr = kt.next();
-                        if (cr.binding.client == app) {
-                            // Binding to ourself is not interesting.
-                            continue;
-                        }
-                        if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
-                            ProcessRecord client = cr.binding.client;
-                            int myHiddenAdj = hiddenAdj;
-                            if (myHiddenAdj > client.hiddenAdj) {
-                                if (client.hiddenAdj >= VISIBLE_APP_ADJ) {
-                                    myHiddenAdj = client.hiddenAdj;
-                                } else {
-                                    myHiddenAdj = VISIBLE_APP_ADJ;
+                        ArrayList<ConnectionRecord> clist = kt.next();
+                        for (int i=0; i<clist.size() && adj > FOREGROUND_APP_ADJ; i++) {
+                            // XXX should compute this based on the max of
+                            // all connected clients.
+                            ConnectionRecord cr = clist.get(i);
+                            if (cr.binding.client == app) {
+                                // Binding to ourself is not interesting.
+                                continue;
+                            }
+                            if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
+                                ProcessRecord client = cr.binding.client;
+                                int myHiddenAdj = hiddenAdj;
+                                if (myHiddenAdj > client.hiddenAdj) {
+                                    if (client.hiddenAdj >= VISIBLE_APP_ADJ) {
+                                        myHiddenAdj = client.hiddenAdj;
+                                    } else {
+                                        myHiddenAdj = VISIBLE_APP_ADJ;
+                                    }
+                                }
+                                int clientAdj = computeOomAdjLocked(
+                                    client, myHiddenAdj, TOP_APP, true);
+                                if (adj > clientAdj) {
+                                    adj = clientAdj >= VISIBLE_APP_ADJ
+                                            ? clientAdj : VISIBLE_APP_ADJ;
+                                    if (!client.hidden) {
+                                        app.hidden = false;
+                                    }
+                                    app.adjType = "service";
+                                    app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                                            .REASON_SERVICE_IN_USE;
+                                    app.adjSource = cr.binding.client;
+                                    app.adjTarget = s.name;
+                                }
+                                if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+                                    if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
+                                        schedGroup = Process.THREAD_GROUP_DEFAULT;
+                                    }
                                 }
                             }
-                            int clientAdj = computeOomAdjLocked(
-                                client, myHiddenAdj, TOP_APP, true);
-                            if (adj > clientAdj) {
-                                adj = clientAdj >= VISIBLE_APP_ADJ
-                                        ? clientAdj : VISIBLE_APP_ADJ;
-                                if (!client.hidden) {
-                                    app.hidden = false;
-                                }
+                            ActivityRecord a = cr.activity;
+                            //if (a != null) {
+                            //    Slog.i(TAG, "Connection to " + a ": state=" + a.state);
+                            //}
+                            if (a != null && adj > FOREGROUND_APP_ADJ &&
+                                    (a.state == ActivityState.RESUMED
+                                     || a.state == ActivityState.PAUSING)) {
+                                adj = FOREGROUND_APP_ADJ;
+                                schedGroup = Process.THREAD_GROUP_DEFAULT;
+                                app.hidden = false;
                                 app.adjType = "service";
                                 app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                                         .REASON_SERVICE_IN_USE;
-                                app.adjSource = cr.binding.client;
+                                app.adjSource = a;
                                 app.adjTarget = s.name;
                             }
-                            if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
-                                if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
-                                    schedGroup = Process.THREAD_GROUP_DEFAULT;
-                                }
-                            }
-                        }
-                        ActivityRecord a = cr.activity;
-                        //if (a != null) {
-                        //    Slog.i(TAG, "Connection to " + a ": state=" + a.state);
-                        //}
-                        if (a != null && adj > FOREGROUND_APP_ADJ &&
-                                (a.state == ActivityState.RESUMED
-                                 || a.state == ActivityState.PAUSING)) {
-                            adj = FOREGROUND_APP_ADJ;
-                            schedGroup = Process.THREAD_GROUP_DEFAULT;
-                            app.hidden = false;
-                            app.adjType = "service";
-                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                    .REASON_SERVICE_IN_USE;
-                            app.adjSource = a;
-                            app.adjTarget = s.name;
                         }
                     }
                 }
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 80a41b7..62be918 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -44,7 +44,7 @@
 /**
  * An entry in the history stack, representing an activity.
  */
-class ActivityRecord extends IApplicationToken.Stub implements UriPermissionOwner {
+class ActivityRecord extends IApplicationToken.Stub {
     final ActivityManagerService service; // owner
     final ActivityStack stack; // owner
     final ActivityInfo info; // all about me
@@ -78,8 +78,7 @@
     HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act
     ArrayList newIntents;   // any pending new intents for single-top mode
     HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
-    HashSet<UriPermission> readUriPermissions; // special access to reading uris.
-    HashSet<UriPermission> writeUriPermissions; // special access to writing uris.
+    UriPermissionOwner uriPermissions; // current special URI access perms.
     ProcessRecord app;  // if non-null, hosting application
     Bitmap thumbnail;       // icon representation of paused screen
     CharSequence description; // textual description of paused screen
@@ -141,11 +140,15 @@
         if (pendingResults != null) {
             pw.print(prefix); pw.print("pendingResults="); pw.println(pendingResults);
         }
-        if (readUriPermissions != null) {
-            pw.print(prefix); pw.print("readUriPermissions="); pw.println(readUriPermissions);
-        }
-        if (writeUriPermissions != null) {
-            pw.print(prefix); pw.print("writeUriPermissions="); pw.println(writeUriPermissions);
+        if (uriPermissions != null) {
+            if (uriPermissions.readUriPermissions != null) {
+                pw.print(prefix); pw.print("readUriPermissions=");
+                        pw.println(uriPermissions.readUriPermissions);
+            }
+            if (uriPermissions.writeUriPermissions != null) {
+                pw.print(prefix); pw.print("writeUriPermissions=");
+                        pw.println(uriPermissions.writeUriPermissions);
+            }
         }
         pw.print(prefix); pw.print("launchFailed="); pw.print(launchFailed);
                 pw.print(" haveState="); pw.print(haveState);
@@ -301,6 +304,13 @@
         }
     }
 
+    UriPermissionOwner getUriPermissionsLocked() {
+        if (uriPermissions == null) {
+            uriPermissions = new UriPermissionOwner(service, this);
+        }
+        return uriPermissions;
+    }
+
     void addResultLocked(ActivityRecord from, String resultWho,
             int requestCode, int resultCode,
             Intent resultData) {
@@ -350,7 +360,7 @@
                 intent = new Intent(intent);
                 ar.add(intent);
                 service.grantUriPermissionFromIntentLocked(callingUid, packageName,
-                        intent, this);
+                        intent, getUriPermissionsLocked());
                 app.thread.scheduleNewIntent(ar, this);
                 sent = true;
             } catch (RemoteException e) {
@@ -367,27 +377,9 @@
     }
 
     void removeUriPermissionsLocked() {
-        if (readUriPermissions != null) {
-            for (UriPermission perm : readUriPermissions) {
-                perm.readOwners.remove(this);
-                if (perm.readOwners.size() == 0 && (perm.globalModeFlags
-                        &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
-                    perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-                    service.removeUriPermissionIfNeededLocked(perm);
-                }
-            }
-            readUriPermissions = null;
-        }
-        if (writeUriPermissions != null) {
-            for (UriPermission perm : writeUriPermissions) {
-                perm.writeOwners.remove(this);
-                if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
-                        &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
-                    perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-                    service.removeUriPermissionIfNeededLocked(perm);
-                }
-            }
-            writeUriPermissions = null;
+        if (uriPermissions != null) {
+            uriPermissions.removeUriPermissionsLocked();
+            uriPermissions = null;
         }
     }
 
@@ -578,38 +570,6 @@
                 state == ActivityState.RESUMED;
      }
     
-    @Override
-    public void addReadPermission(UriPermission perm) {
-        if (readUriPermissions == null) {
-            readUriPermissions = new HashSet<UriPermission>();
-        }
-        readUriPermissions.add(perm);
-    }
-
-    @Override
-    public void addWritePermission(UriPermission perm) {
-        if (writeUriPermissions == null) {
-            writeUriPermissions = new HashSet<UriPermission>();
-        }
-        writeUriPermissions.add(perm);
-    }
-
-    @Override
-    public void removeReadPermission(UriPermission perm) {
-        readUriPermissions.remove(perm);
-        if (readUriPermissions.size() == 0) {
-            readUriPermissions = null;
-        }
-    }
-
-    @Override
-    public void removeWritePermission(UriPermission perm) {
-        writeUriPermissions.remove(perm);
-        if (writeUriPermissions.size() == 0) {
-            writeUriPermissions = null;
-        }
-    }
-    
     public String toString() {
         if (stringName != null) {
             return stringName;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index a5f7e96..a99b48c 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -2327,12 +2327,12 @@
         if (grantedUriPermissions != null && callingUid > 0) {
             for (int i=0; i<grantedUriPermissions.length; i++) {
                 mService.grantUriPermissionLocked(callingUid, r.packageName,
-                        grantedUriPermissions[i], grantedMode, r);
+                        grantedUriPermissions[i], grantedMode, r.getUriPermissionsLocked());
             }
         }
 
         mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
-                intent, r);
+                intent, r.getUriPermissionsLocked());
 
         if (newTask) {
             EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId);
@@ -2557,7 +2557,7 @@
 
         if (callingUid > 0) {
             mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
-                    data, r);
+                    data, r.getUriPermissionsLocked());
         }
 
         if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
@@ -2885,7 +2885,7 @@
                     + " res=" + resultCode + " data=" + resultData);
             if (r.info.applicationInfo.uid > 0) {
                 mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
-                        r.packageName, resultData, r);
+                        r.packageName, resultData, r.getUriPermissionsLocked());
             }
             resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
                                      resultData);
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 255fbe3..f35a68e 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -72,8 +72,8 @@
     final HashMap<Intent.FilterComparison, IntentBindRecord> bindings
             = new HashMap<Intent.FilterComparison, IntentBindRecord>();
                             // All active bindings to the service.
-    final HashMap<IBinder, ConnectionRecord> connections
-            = new HashMap<IBinder, ConnectionRecord>();
+    final HashMap<IBinder, ArrayList<ConnectionRecord>> connections
+            = new HashMap<IBinder, ArrayList<ConnectionRecord>>();
                             // IBinder -> ConnectionRecord of all bound clients
 
     ProcessRecord app;      // where this service is running or null.
@@ -96,7 +96,7 @@
 
     String stringName;      // caching of toString
     
-    static class StartItem implements UriPermissionOwner {
+    static class StartItem {
         final ServiceRecord sr;
         final int id;
         final Intent intent;
@@ -104,12 +104,10 @@
         long deliveredTime;
         int deliveryCount;
         int doneExecutingCount;
+        UriPermissionOwner uriPermissions;
 
         String stringName;      // caching of toString
 
-        HashSet<UriPermission> readUriPermissions; // special access to reading uris.
-        HashSet<UriPermission> writeUriPermissions; // special access to writing uris.
-
         StartItem(ServiceRecord _sr, int _id, Intent _intent, int _targetPermissionUid) {
             sr = _sr;
             id = _id;
@@ -117,60 +115,17 @@
             targetPermissionUid = _targetPermissionUid;
         }
 
+        UriPermissionOwner getUriPermissionsLocked() {
+            if (uriPermissions == null) {
+                uriPermissions = new UriPermissionOwner(sr.ams, this);
+            }
+            return uriPermissions;
+        }
+
         void removeUriPermissionsLocked() {
-            if (readUriPermissions != null) {
-                for (UriPermission perm : readUriPermissions) {
-                    perm.readOwners.remove(this);
-                    if (perm.readOwners.size() == 0 && (perm.globalModeFlags
-                            &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
-                        perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-                        sr.ams.removeUriPermissionIfNeededLocked(perm);
-                    }
-                }
-                readUriPermissions = null;
-            }
-            if (writeUriPermissions != null) {
-                for (UriPermission perm : writeUriPermissions) {
-                    perm.writeOwners.remove(this);
-                    if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
-                            &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
-                        perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-                        sr.ams.removeUriPermissionIfNeededLocked(perm);
-                    }
-                }
-                writeUriPermissions = null;
-            }
-        }
-
-        @Override
-        public void addReadPermission(UriPermission perm) {
-            if (readUriPermissions == null) {
-                readUriPermissions = new HashSet<UriPermission>();
-            }
-            readUriPermissions.add(perm);
-        }
-
-        @Override
-        public void addWritePermission(UriPermission perm) {
-            if (writeUriPermissions == null) {
-                writeUriPermissions = new HashSet<UriPermission>();
-            }
-            writeUriPermissions.add(perm);
-        }
-
-        @Override
-        public void removeReadPermission(UriPermission perm) {
-            readUriPermissions.remove(perm);
-            if (readUriPermissions.size() == 0) {
-                readUriPermissions = null;
-            }
-        }
-
-        @Override
-        public void removeWritePermission(UriPermission perm) {
-            writeUriPermissions.remove(perm);
-            if (writeUriPermissions.size() == 0) {
-                writeUriPermissions = null;
+            if (uriPermissions != null) {
+                uriPermissions.removeUriPermissionsLocked();
+                uriPermissions = null;
             }
         }
 
@@ -218,13 +173,15 @@
                 pw.print(prefix); pw.print("  targetPermissionUid=");
                         pw.println(si.targetPermissionUid);
             }
-            if (si.readUriPermissions != null) {
-                pw.print(prefix); pw.print("  readUriPermissions=");
-                        pw.println(si.readUriPermissions);
-            }
-            if (si.writeUriPermissions != null) {
-                pw.print(prefix); pw.print("  writeUriPermissions=");
-                        pw.println(si.writeUriPermissions);
+            if (si.uriPermissions != null) {
+                if (si.uriPermissions.readUriPermissions != null) {
+                    pw.print(prefix); pw.print("  readUriPermissions=");
+                            pw.println(si.uriPermissions.readUriPermissions);
+                }
+                if (si.uriPermissions.writeUriPermissions != null) {
+                    pw.print(prefix); pw.print("  writeUriPermissions=");
+                            pw.println(si.uriPermissions.writeUriPermissions);
+                }
             }
         }
     }
@@ -296,10 +253,12 @@
         }
         if (connections.size() > 0) {
             pw.print(prefix); pw.println("All Connections:");
-            Iterator<ConnectionRecord> it = connections.values().iterator();
+            Iterator<ArrayList<ConnectionRecord>> it = connections.values().iterator();
             while (it.hasNext()) {
-                ConnectionRecord c = it.next();
-                pw.print(prefix); pw.print("  "); pw.println(c);
+                ArrayList<ConnectionRecord> c = it.next();
+                for (int i=0; i<c.size(); i++) {
+                    pw.print(prefix); pw.print("  "); pw.println(c.get(i));
+                }
             }
         }
     }
diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index 93c59cc..c95546e 100644
--- a/services/java/com/android/server/am/UriPermission.java
+++ b/services/java/com/android/server/am/UriPermission.java
@@ -22,13 +22,14 @@
 import java.io.PrintWriter;
 import java.util.HashSet;
 
-interface UriPermissionOwner {
-    void addReadPermission(UriPermission perm);
-    void addWritePermission(UriPermission perm);
-    void removeReadPermission(UriPermission perm);
-    void removeWritePermission(UriPermission perm);
-}
-
+/**
+ * Description of a permission granted to an app to access a particular URI.
+ *
+ * CTS tests for this functionality can be run with "runtest cts-appsecurity".
+ *
+ * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert
+ *      /src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+ */
 class UriPermission {
     final int uid;
     final Uri uri;
diff --git a/services/java/com/android/server/am/UriPermissionOwner.java b/services/java/com/android/server/am/UriPermissionOwner.java
new file mode 100644
index 0000000..99c82e6
--- /dev/null
+++ b/services/java/com/android/server/am/UriPermissionOwner.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+
+import java.util.HashSet;
+import java.util.Iterator;
+
+class UriPermissionOwner {
+    final ActivityManagerService service;
+    final Object owner;
+
+    Binder externalToken;
+
+    HashSet<UriPermission> readUriPermissions; // special access to reading uris.
+    HashSet<UriPermission> writeUriPermissions; // special access to writing uris.
+
+    class ExternalToken extends Binder {
+        UriPermissionOwner getOwner() {
+            return UriPermissionOwner.this;
+        }
+    }
+
+    UriPermissionOwner(ActivityManagerService _service, Object _owner) {
+        service = _service;
+        owner = _owner;
+    }
+
+    Binder getExternalTokenLocked() {
+        if (externalToken != null) {
+            externalToken = new ExternalToken();
+        }
+        return externalToken;
+    }
+
+    static UriPermissionOwner fromExternalToken(IBinder token) {
+        if (token instanceof ExternalToken) {
+            return ((ExternalToken)token).getOwner();
+        }
+        return null;
+    }
+
+    void removeUriPermissionsLocked() {
+        removeUriPermissionsLocked(Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    void removeUriPermissionsLocked(int mode) {
+        if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
+                && readUriPermissions != null) {
+            for (UriPermission perm : readUriPermissions) {
+                perm.readOwners.remove(this);
+                if (perm.readOwners.size() == 0 && (perm.globalModeFlags
+                        &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+                    perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+                    service.removeUriPermissionIfNeededLocked(perm);
+                }
+            }
+            readUriPermissions = null;
+        }
+        if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0
+                && writeUriPermissions != null) {
+            for (UriPermission perm : writeUriPermissions) {
+                perm.writeOwners.remove(this);
+                if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
+                        &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
+                    perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+                    service.removeUriPermissionIfNeededLocked(perm);
+                }
+            }
+            writeUriPermissions = null;
+        }
+    }
+
+    void removeUriPermissionLocked(Uri uri, int mode) {
+        if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
+                && readUriPermissions != null) {
+            Iterator<UriPermission> it = readUriPermissions.iterator();
+            while (it.hasNext()) {
+                UriPermission perm = it.next();
+                if (uri.equals(perm.uri)) {
+                    perm.readOwners.remove(this);
+                    if (perm.readOwners.size() == 0 && (perm.globalModeFlags
+                            &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+                        perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+                        service.removeUriPermissionIfNeededLocked(perm);
+                    }
+                    it.remove();
+                }
+            }
+            if (readUriPermissions.size() == 0) {
+                readUriPermissions = null;
+            }
+        }
+        if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0
+                && writeUriPermissions != null) {
+            Iterator<UriPermission> it = writeUriPermissions.iterator();
+            while (it.hasNext()) {
+                UriPermission perm = it.next();
+                if (uri.equals(perm.uri)) {
+                    perm.writeOwners.remove(this);
+                    if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
+                            &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
+                        perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+                        service.removeUriPermissionIfNeededLocked(perm);
+                    }
+                    it.remove();
+                }
+            }
+            if (writeUriPermissions.size() == 0) {
+                writeUriPermissions = null;
+            }
+        }
+    }
+
+    public void addReadPermission(UriPermission perm) {
+        if (readUriPermissions == null) {
+            readUriPermissions = new HashSet<UriPermission>();
+        }
+        readUriPermissions.add(perm);
+    }
+
+    public void addWritePermission(UriPermission perm) {
+        if (writeUriPermissions == null) {
+            writeUriPermissions = new HashSet<UriPermission>();
+        }
+        writeUriPermissions.add(perm);
+    }
+
+    public void removeReadPermission(UriPermission perm) {
+        readUriPermissions.remove(perm);
+        if (readUriPermissions.size() == 0) {
+            readUriPermissions = null;
+        }
+    }
+
+    public void removeWritePermission(UriPermission perm) {
+        writeUriPermissions.remove(perm);
+        if (writeUriPermissions.size() == 0) {
+            writeUriPermissions = null;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return owner.toString();
+    }
+}
diff --git a/services/java/com/android/server/sip/SipService.java b/services/java/com/android/server/sip/SipService.java
index 1142136..3dcaff6 100644
--- a/services/java/com/android/server/sip/SipService.java
+++ b/services/java/com/android/server/sip/SipService.java
@@ -32,6 +32,7 @@
 import android.net.sip.SipSessionAdapter;
 import android.net.sip.SipSessionState;
 import android.net.wifi.WifiManager;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -97,6 +98,7 @@
     }
 
     public void open(SipProfile localProfile) {
+        localProfile.setCallingUid(Binder.getCallingUid());
         if (localProfile.getAutoRegistration()) {
             openToReceiveCalls(localProfile);
         } else {
@@ -119,6 +121,7 @@
 
     public synchronized void open3(SipProfile localProfile,
             String incomingCallBroadcastAction, ISipSessionListener listener) {
+        localProfile.setCallingUid(Binder.getCallingUid());
         if (TextUtils.isEmpty(incomingCallBroadcastAction)) {
             throw new RuntimeException(
                     "empty broadcast action for incoming call");
@@ -165,6 +168,7 @@
 
     public synchronized ISipSession createSession(SipProfile localProfile,
             ISipSessionListener listener) {
+        localProfile.setCallingUid(Binder.getCallingUid());
         if (!mConnected) return null;
         try {
             SipSessionGroupExt group = createGroup(localProfile);
@@ -362,16 +366,7 @@
 
         private SipProfile duplicate(SipProfile p) {
             try {
-                return new SipProfile.Builder(p.getUserName(), p.getSipDomain())
-                        .setProfileName(p.getProfileName())
-                        .setPassword("*")
-                        .setPort(p.getPort())
-                        .setProtocol(p.getProtocol())
-                        .setOutboundProxy(p.getProxyAddress())
-                        .setSendKeepAlive(p.getSendKeepAlive())
-                        .setAutoRegistration(p.getAutoRegistration())
-                        .setDisplayName(p.getDisplayName())
-                        .build();
+                return new SipProfile.Builder(p).setPassword("*").build();
             } catch (Exception e) {
                 Log.wtf(TAG, "duplicate()", e);
                 throw new RuntimeException("duplicate profile", e);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 212c08b..194c295 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -163,9 +163,13 @@
     const uint32_t hwFlags = hw.getFlags();
     
     mFormat = format;
-    mReqFormat = format;
     mWidth  = w;
     mHeight = h;
+
+    mReqFormat = format;
+    mReqWidth = w;
+    mReqHeight = h;
+
     mSecure = (flags & ISurfaceComposer::eSecure) ? true : false;
     mNeedsBlending = (info.h_alpha - info.l_alpha) > 0;
 
@@ -253,12 +257,16 @@
     } else {
 slowpath:
         GGLSurface t;
-        status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
-        LOGE_IF(res, "error %d (%s) locking buffer %p",
-                res, strerror(res), buffer.get());
-        if (res == NO_ERROR) {
-            mBufferManager.loadTexture(dirty, t);
-            buffer->unlock();
+        if (buffer->usage & GRALLOC_USAGE_SW_READ_MASK) {
+            status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
+            LOGE_IF(res, "error %d (%s) locking buffer %p",
+                    res, strerror(res), buffer.get());
+            if (res == NO_ERROR) {
+                mBufferManager.loadTexture(dirty, t);
+                buffer->unlock();
+            }
+        } else {
+            // we can't do anything
         }
     }
 }
@@ -357,16 +365,22 @@
     uint32_t w, h, f;
     { // scope for the lock
         Mutex::Autolock _l(mLock);
-        const bool fixedSizeChanged = mFixedSize != (reqWidth && reqHeight);
-        const bool formatChanged    = mReqFormat != reqFormat;
-        mReqWidth  = reqWidth;
-        mReqHeight = reqHeight;
-        mReqFormat = reqFormat;
-        mFixedSize = reqWidth && reqHeight;
-        w = reqWidth  ? reqWidth  : mWidth;
-        h = reqHeight ? reqHeight : mHeight;
-        f = reqFormat ? reqFormat : mFormat;
-        if (fixedSizeChanged || formatChanged) {
+
+        // zero means default
+        if (!reqFormat) reqFormat = mFormat;
+        if (!reqWidth)  reqWidth = mWidth;
+        if (!reqHeight) reqHeight = mHeight;
+
+        w = reqWidth;
+        h = reqHeight;
+        f = reqFormat;
+
+        if ((reqWidth != mReqWidth) || (reqHeight != mReqHeight) ||
+                (reqFormat != mReqFormat)) {
+            mReqWidth  = reqWidth;
+            mReqHeight = reqHeight;
+            mReqFormat = reqFormat;
+
             lcblk->reallocateAllExcept(index);
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index 980affa..12df44e 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -691,8 +691,8 @@
      * Stop the playing DTMF tone. Ignored if there is no playing DTMF
      * tone or no active call.
      */
-    public void stopDtmf(Phone phone) {
-        phone.stopDtmf();
+    public void stopDtmf() {
+        if (hasActiveFgCall()) getFgPhone().stopDtmf();
     }
 
     /**
@@ -709,7 +709,7 @@
      * @param onComplete is the callback message when the action is processed by BP
      *
      */
-    public boolean sendBurstDtmf(Phone phone, String dtmfString, int on, int off, Message onComplete) {
+    public boolean sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) {
         if (hasActiveFgCall()) {
             getActiveFgCall().getPhone().sendBurstDtmf(dtmfString, on, off, onComplete);
             return true;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index b39556a..8d77f76 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -224,6 +224,7 @@
             mCM.unregisterForNVReady(this); //EVENT_NV_READY
             mSST.unregisterForNetworkAttach(this); //EVENT_REGISTERED_TO_NETWORK
             mCM.unSetOnSuppServiceNotification(this);
+            removeCallbacks(mExitEcmRunnable);
 
             mPendingMmis.clear();
 
@@ -256,6 +257,7 @@
             this.mSST = null;
             this.mEriManager = null;
             this.mCcatService = null;
+            this.mExitEcmRunnable = null;
     }
 
     protected void finalize() {
diff --git a/tests/CoreTests/android/core/JavaPerformanceTests.java b/tests/CoreTests/android/core/JavaPerformanceTests.java
index fbe70cc..95075ea 100644
--- a/tests/CoreTests/android/core/JavaPerformanceTests.java
+++ b/tests/CoreTests/android/core/JavaPerformanceTests.java
@@ -23,7 +23,6 @@
 
     public static String[] children() {
         return new String[] {
-                StringTest.class.getName(),
                 HashMapPerformanceTest.class.getName(),
                 ArrayListPerformanceTest.class.getName(),
                 TreeMapPerformanceTest.class.getName(),
diff --git a/tests/CoreTests/android/core/StringTest.java b/tests/CoreTests/android/core/StringTest.java
deleted file mode 100644
index 128531c..0000000
--- a/tests/CoreTests/android/core/StringTest.java
+++ /dev/null
@@ -1,951 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.core;
-
-import java.util.Locale;
-
-import android.test.PerformanceTestBase;
-import android.test.PerformanceTestCase;
-
-public class StringTest extends PerformanceTestBase {
-    public static final int ITERATIONS = 1000;
-    public static final String STATIC_STRING_01 = "Hello Android";
-    public static final String STATIC_STRING_02 =
-            "Remember, today is the tomorrow you worried about yesterday";
-    public static final char[] STATIC_CHAR_ARRAY =
-            {'N', 'A', 'N', 'D', 'R', 'O', 'I', 'D'};
-    public static StringBuffer STATIC_SBUF = new StringBuffer(STATIC_STRING_02);
-
-    @Override
-    public int startPerformance(PerformanceTestCase.Intermediates intermediates) {
-        intermediates.setInternalIterations(ITERATIONS);
-        return 0;
-    }
-
-    /** Create an empty String object* */
-
-    public void testStringCreate() {
-        String rString;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-            rString = new String();
-        }
-    }
-
-    /** Create an initialised String object* */
-
-    public void testStringCreate1() {
-        String rString, str = STATIC_STRING_01;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str);
-            rString = new String(str); // 10
-        }
-    }
-
-    /** equals() with for loop* */
-    public void testStringEquals() {
-        String mString = new String(STATIC_STRING_01);
-        String str = STATIC_STRING_01;
-        boolean result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-            result = mString.equals(str);
-        }
-    }
-
-    /**
-     * ContentEquals- Comparing the content of a String with that of a String
-     * Buffer*
-     */
-
-    public void testStringContentEquals() {
-        StringBuffer sBuf = new StringBuffer(STATIC_STRING_01);
-        String str = STATIC_STRING_01;
-        boolean result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-            result = str.contentEquals(sBuf);
-        }
-    }
-
-    /** Compare string objects lexicographically using compareTo() with for loop* */
-
-    public void testStringCompareTo() {
-        String str1 = new String(STATIC_STRING_01);
-        String str2 = STATIC_STRING_01;
-        int result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-            result = str1.compareTo(str2);
-        }
-
-    }
-
-    /** Compare string objects using compareToIgnorecase() with for loop* */
-
-    public void testStringCompareToIgnoreCase() {
-        String mString = new String(STATIC_STRING_01);
-        String str2 = STATIC_STRING_01;
-        int result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-            result = mString.compareToIgnoreCase(str2);
-        }
-    }
-
-    /** startsWith * */
-
-    public void testStringstartsWith() {
-        boolean result;
-        String str1 = STATIC_STRING_02, str2 = "Rem";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-            result = str1.startsWith(str2);
-        }
-    }
-
-    /** startsWith(String seq, int begin) * */
-
-    public void testStringstartsWith1() {
-        String str1 = STATIC_STRING_02, str2 = "tom";
-        int pos = 10;
-        boolean result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-            result = str1.startsWith(str2, pos);
-        }
-    }
-
-    /** endsWith * */
-
-    public void testStringendsWith() {
-        String str = STATIC_STRING_02, str1 = "day";
-        boolean result;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-            result = str.endsWith(str1);
-        }
-    }
-
-    /**
-     * indexOf to determine whether a string contains a substring
-     */
-    public void testStringindexOf() {
-        boolean result;
-        String str = STATIC_STRING_02, str1 = "tomo";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-            result = str.indexOf(str1) > 0;
-        }
-    }
-
-    /** indexOf()* */
-
-    public void testStringindexOf1() {
-        int index;
-        String str = STATIC_STRING_02;
-        char c = 't';
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-            index = str.indexOf(c);
-        }
-
-    }
-
-    /** indexOf(char c, int start)* */
-    public void testStringindexOf2() {
-        int index, pos = 12;
-        String str = STATIC_STRING_02, str1 = "tom";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-            index = str.indexOf(str1, pos);
-        }
-    }
-
-    /** lastIndexOf()* */
-
-    public void testStringlastIndexOf() {
-        int index;
-        char c = 't';
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-            index = str.lastIndexOf(c);
-        }
-    }
-
-    /** lastIndexOf()* */
-
-    public void testStringlastIndexOf1() {
-        int index, pos = 36;
-        String str = STATIC_STRING_02, str1 = "tom";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-            index = str.lastIndexOf(str1, pos);
-        }
-    }
-
-    /**
-     * contains() to determine whether a string contains a substring
-     */
-
-    public void testStringcontains() {
-        boolean result;
-        String str = STATIC_STRING_02, str1 = "tomo";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-            result = str.contains(str1);
-        }
-    }
-
-    /** substring(int start) */
-
-    public void testStringsubstring() {
-        String rString;
-        String str = STATIC_STRING_02;
-        int index = 10;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-            rString = str.substring(index);
-        }
-    }
-
-    /** substring(int start, int end) in a for loop* */
-
-    public void testStringsubstring1() {
-        String rString;
-        String str = STATIC_STRING_02;
-        int start = 10, end = 48;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-            rString = str.substring(start, end);
-        }
-    }
-
-    /**
-     * valueOf(char[] cArray) String representation of a character array
-     */
-    public void testStringvalueOf() {
-        String rString;
-        char[] cArray = STATIC_CHAR_ARRAY;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-            rString = String.valueOf(cArray);
-        }
-    }
-
-    /** valueOf(char[] cArray, int offset, int count)* */
-
-    public void testStringvalueOf1() {
-        String rString;
-        char[] cArray = STATIC_CHAR_ARRAY;
-        int start = 1, end = 7;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-            rString = String.valueOf(cArray, start, end);
-        }
-    }
-
-    /** Convert a string to a char Array* */
-
-    public void testStringtoCharArray() {
-        char[] cArray;
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-            cArray = str.toCharArray();
-        }
-    }
-
-    /** length()* */
-
-    public void testStringlength() {
-        int len;
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-            len = str.length();
-        }
-    }
-
-    /** hashcode()* */
-
-    public void testStringhashCode() {
-        int index;
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-            index = str.hashCode();
-        }
-    }
-
-    /** replace()* */
-
-    public void testStringreplace() {
-        String rString;
-        String str = STATIC_STRING_02;
-        char c1 = ' ', c2 = ' ';
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-            rString = str.replace(c1, c2);
-        }
-    }
-
-    public void testStringreplaceAll() {
-        String rString;
-        String str = STATIC_STRING_02, str1 = " ", str2 = "/";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-            rString = str.replaceAll(str1, str2);
-        }
-    }
-
-    /** Convert a StringBuffer to a String* */
-
-    public void testStringtoString() {
-        StringBuffer sBuf = new StringBuffer(STATIC_STRING_02);
-
-        String rString;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-            rString = sBuf.toString();
-        }
-    }
-
-    /** Split a string into an array of strings* */
-
-    public void testStringsplit() {
-        String[] strings;
-        String str1 = STATIC_STRING_02, str = " ";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-            strings = str1.split(str);
-
-        }
-    }
-
-    /** Split a string into an array of strings* */
-
-    public void testStringsplit1() {
-        String str = STATIC_STRING_02, str1 = " ";
-        String[] strings;
-        int pos = 8;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-            strings = str.split(str1, pos);
-        }
-    }
-
-    public void testStringgetBytes() {
-        byte[] bytes;
-        String str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-            bytes = str.getBytes();
-        }
-    }
-
-    /** copyValueOf(char[] data) * */
-
-    public void testStringcopyValueOf() {
-        String rString;
-        char[] cArray = STATIC_CHAR_ARRAY;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-            rString = String.copyValueOf(cArray);
-        }
-    }
-
-    /** copyValueOf(char[] data, int index, int count)* */
-
-    public void testStringcopyValueOf1() {
-        String rString;
-        int start = 1, end = 7;
-        char[] cArray = STATIC_CHAR_ARRAY;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-            rString = String.copyValueOf(cArray, start, end);
-        }
-    }
-
-    /** trim()* */
-
-    public void testStringtrim() {
-        String mString =
-                new String(
-                        "                            HELLO ANDROID                                                ");
-        String rString;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-            rString = mString.trim();
-        }
-    }
-
-    /** getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)* */
-
-    public void testStringgetChars() {
-        char[] cArray = STATIC_CHAR_ARRAY;
-        String str = STATIC_STRING_01;
-        int value1 = 7, value2 = 12, value3 = 1;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-            str.getChars(value1, value2, cArray, value3);
-        }
-    }
-
-    /** toUpperCase()* */
-
-    public void testStringtoUpperCase() {
-        String rString, str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-            rString = str.toUpperCase();
-        }
-    }
-
-    /** toUpperCase() with locale* */
-
-    public void testStringtoUpperCase1() {
-        Locale locale = new Locale("tr");
-        String str = STATIC_STRING_02;
-        String rString;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-            rString = str.toUpperCase(locale);
-        }
-    }
-
-    /** toLowerCase* */
-
-    public void StringtoLowerCase() {
-        String rString, str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-            rString = str.toLowerCase();
-        }
-    }
-
-    /** toLowerCase with locale* */
-
-    public void testStringtoLowerCase1() {
-        Locale locale = new Locale("tr");
-        String rString, str = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-            rString = str.toLowerCase(locale);
-        }
-    }
-
-    /** charAt()* */
-
-    public void testStringcharAt() {
-        String str = STATIC_STRING_02;
-        int index, pos = 21;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-            index = str.charAt(pos);
-        }
-    }
-
-    public void testStringConcat() {
-        String mString, str1 = STATIC_STRING_01, str2 = STATIC_STRING_02;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-            mString = str1.concat(str2);
-        }
-    }
-
-    public void testStringBufferAppend() {
-        StringBuffer sBuf = new StringBuffer(" ");
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-            sBuf.append(i);
-        }
-    }
-
-    public void testStringBufferInsert() {
-        StringBuffer sBuf = new StringBuffer(" ");
-        int index = sBuf.length();
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-            sBuf.insert(index, i);
-        }
-    }
-
-    public void testStringBufferReverse() {
-        StringBuffer sBuf = STATIC_SBUF;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-            sBuf.reverse();
-        }
-    }
-
-    public void testStringBufferSubstring() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String rString;
-        int index = 0;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-            rString = sBuf.substring(index);
-        }
-    }
-
-    public void testStringBufferSubstring1() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String rString;
-        int start = 5, end = 25;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-            rString = sBuf.substring(start, end);
-        }
-    }
-
-    public void testStringBufferReplace() {
-        StringBuffer sBuf = STATIC_SBUF;
-        int start = 3, end = 6;
-        String str = "ind";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-            sBuf.replace(start, end, str);
-        }
-    }
-
-    public void testStringBufferIndexOf() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String str = "t";
-        int index;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-            index = sBuf.indexOf(str);
-        }
-    }
-
-    public void testStringBufferIndexOf1() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String str = "tom";
-        int index, pos = 12;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-            index = sBuf.indexOf(str, pos);
-        }
-
-    }
-
-    public void testStringBufferLastIndexOf() {
-        StringBuffer sBuf = STATIC_SBUF;
-        String str = "t";
-        int index;
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-            index = sBuf.lastIndexOf(str);
-        }
-    }
-
-    public void testStringBufferLastIndexOf1() {
-        StringBuffer sBuf = STATIC_SBUF;
-        int index, pos = 36;
-        String str = "tom";
-        for (int i = ITERATIONS - 1; i >= 0; i--) {
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-            index = sBuf.lastIndexOf(str, pos);
-        }
-    }
-}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java b/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
index 0c2347d..716086b 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
@@ -16,14 +16,12 @@
 
 package com.android.dumprendertree;
 
-import android.os.Handler;
 import android.os.SystemClock;
 import android.util.*;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.webkit.WebView;
 
-import java.lang.InterruptedException;
 import java.util.Arrays;
 import java.util.Vector;
 
@@ -33,7 +31,7 @@
 	
     WebViewEventSender(WebView webView) {
         mWebView = webView;
-        mTouchPoints = new Vector();
+        mTouchPoints = new Vector<TouchPoint>();
     }
 	
 	public void resetMouse() {
@@ -82,47 +80,23 @@
 		mouseUp();
 	}
 
-	public void mouseDown() {
-          /*  KeyEvent event = new KeyEvent(
-                KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER);
-            mWebView.onKeyDown(event.getKeyCode(), event); */
-	}
+    public void mouseDown() {
+        long ts = SystemClock.uptimeMillis();
+        MotionEvent event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_DOWN, mouseX, mouseY, 0);
+        mWebView.onTouchEvent(event);
+    }
 
-	public void mouseMoveTo(int X, int Y) {
-		if (X > mouseX) {
-                    KeyEvent event = new KeyEvent(
-                        KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
-                    mWebView.onKeyDown(event.getKeyCode(), event);
-                    mWebView.onKeyUp(event.getKeyCode(), event);
-		} else if ( X < mouseX ) {
-                    KeyEvent event = new KeyEvent(
-                        KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
-                    mWebView.onKeyDown(event.getKeyCode(), event);
-                    mWebView.onKeyUp(event.getKeyCode(), event);
-		}
-		if (Y > mouseY) {
-                    KeyEvent event = new KeyEvent(
-                        KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN);
-                    mWebView.onKeyDown(event.getKeyCode(), event);
-                    mWebView.onKeyUp(event.getKeyCode(), event);
-		} else if (Y < mouseY ) {
-                    KeyEvent event = new KeyEvent(
-                        KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP);
-                    mWebView.onKeyDown(event.getKeyCode(), event);
-                    mWebView.onKeyUp(event.getKeyCode(), event);
-		}
-		mouseX= X;
-		mouseY= Y;
-	
-	}
+    public void mouseMoveTo(int X, int Y) {
+        mouseX= X;
+        mouseY= Y;
+    }
 
-	public void mouseUp() {
-        /*    KeyEvent event = new KeyEvent(
-                KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER);
-            mWebView.onKeyDown(event.getKeyCode(), event);*/
+     public void mouseUp() {
+        long ts = SystemClock.uptimeMillis();
+        MotionEvent event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_UP, mouseX, mouseY, 0);
+        mWebView.onTouchEvent(event);
+    }
 
-	}
-	
 	// Assumes lowercase chars, case needs to be
 	// handled by calling function.
 	static int keyMapper(char c) {
@@ -365,7 +339,7 @@
             mX = x;
             mY = y;
         }
-    };
+    }
 
     private Vector<TouchPoint> mTouchPoints;
     private int mTouchMetaState;
diff --git a/tests/DumpRenderTree2/assets/run_apache2.py b/tests/DumpRenderTree2/assets/run_apache2.py
index f404090..5764f0e 100755
--- a/tests/DumpRenderTree2/assets/run_apache2.py
+++ b/tests/DumpRenderTree2/assets/run_apache2.py
@@ -33,7 +33,7 @@
   else:
     run_cmd = sys.argv[1]
 
-  #Setup logging class
+  # Setup logging class
   logging.basicConfig(level=logging.INFO, format='%(message)s')
 
   if not run_cmd in ("start", "stop", "restart"):
@@ -41,55 +41,69 @@
     logging.info("Usage: python run_apache2.py start|stop|restart")
     return
 
-  #Create /tmp/WebKit if it doesn't exist. This is needed for various files used by apache2
+  # Create /tmp/WebKit if it doesn't exist. This is needed for various files used by apache2
   tmp_WebKit = os.path.join("/tmp", "WebKit")
   if not os.path.exists(tmp_WebKit):
     os.mkdir(tmp_WebKit)
 
-  #Get the path to android tree root based on the script location.
-  #Basically we go 5 levels up
+  # Get the path to android tree root based on the script location.
+  # Basically we go 5 levels up
   parent = os.pardir
   script_location = os.path.abspath(os.path.dirname(sys.argv[0]))
   android_tree_root = os.path.join(script_location, parent, parent, parent, parent, parent)
   android_tree_root = os.path.normpath(android_tree_root)
 
-  #Prepare the command to set ${APACHE_RUN_USER} and ${APACHE_RUN_GROUP}
+  # Paths relative to android_tree_root
+  webkit_path = os.path.join("external", "webkit")
+  layout_tests_path = os.path.join(webkit_path, "LayoutTests")
+  http_conf_path = os.path.join(layout_tests_path, "http", "conf")
+
+  # Prepare the command to set ${APACHE_RUN_USER} and ${APACHE_RUN_GROUP}
   envvars_path = os.path.join("/etc", "apache2", "envvars")
   export_envvars_cmd = "source " + envvars_path
 
   error_log_path = os.path.join(tmp_WebKit, "apache2-error.log")
+  custom_log_path = os.path.join(tmp_WebKit, "apache2-access.log")
 
-  #Prepare the command to (re)start/stop the server with specified settings
+  # Prepare the command to (re)start/stop the server with specified settings
   apache2_restart_cmd = "apache2 -k " + run_cmd
   directives  = " -c \"ServerRoot " + android_tree_root + "\""
-  directives += " -c \"DocumentRoot " + os.path.join("external", "webkit") + "\""
 
-  #This directive is commented out in apache2-debian-httpd.conf for some reason
-  #However, it is useful to browse through tests in the browser, so it's added here.
-  #One thing to note is that because of problems with mod_dir and port numbers, mod_dir
-  #is turned off. That means that there _must_ be a trailing slash at the end of URL
-  #for auto indexes to work correctly.
+  # We use http/tests as the document root as the HTTP tests use hardcoded
+  # resources at the server root. We then use aliases to make available the
+  # complete set of tests and the required scripts.
+  directives += " -c \"DocumentRoot " + os.path.join(layout_tests_path, "http", "tests/") + "\""
+  directives += " -c \"Alias /LayoutTests " + layout_tests_path + "\""
+  directives += " -c \"Alias /WebKitTools/DumpRenderTree/android " + \
+    os.path.join(webkit_path, "WebKitTools", "DumpRenderTree", "android") + "\""
+
+  # This directive is commented out in apache2-debian-httpd.conf for some reason
+  # However, it is useful to browse through tests in the browser, so it's added here.
+  # One thing to note is that because of problems with mod_dir and port numbers, mod_dir
+  # is turned off. That means that there _must_ be a trailing slash at the end of URL
+  # for auto indexes to work correctly.
   directives += " -c \"LoadModule autoindex_module /usr/lib/apache2/modules/mod_autoindex.so\""
 
   directives += " -c \"ErrorLog " + error_log_path +"\""
-  directives += " -c \"SSLCertificateFile " + os.path.join ("external", "webkit", "LayoutTests",
-    "http", "conf", "webkit-httpd.pem") + "\""
+  directives += " -c \"CustomLog " + custom_log_path + " combined\""
+  directives += " -c \"SSLCertificateFile " + os.path.join(http_conf_path, "webkit-httpd.pem") + \
+    "\""
   directives += " -c \"User ${APACHE_RUN_USER}\""
   directives += " -c \"Group ${APACHE_RUN_GROUP}\""
   directives += " -C \"TypesConfig " + os.path.join("/etc", "mime.types") + "\""
-  conf_file_cmd = " -f " + os.path.join(android_tree_root, "external", "webkit", "LayoutTests",
-    "http", "conf", "apache2-debian-httpd.conf")
+  conf_file_cmd = " -f " + \
+    os.path.join(android_tree_root, http_conf_path, "apache2-debian-httpd.conf")
 
-  #Try to execute the commands
+  # Try to execute the commands
   logging.info("Will " + run_cmd + " apache2 server.")
   cmd = export_envvars_cmd + " && " + apache2_restart_cmd + directives + conf_file_cmd
   p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
   (out, err) = p.communicate()
 
-  #Output the stdout from the command to console
+  # Output the stdout from the command to console
   logging.info(out)
 
-  #Report any errors
+  # Report any errors
   if p.returncode != 0:
     logging.info("!! ERRORS:")
 
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java
index 8fca629..bb9a916 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java
@@ -63,7 +63,8 @@
                 port = url.getPort();
             }
         } catch (MalformedURLException e) {
-            Log.e(LOG_TAG + "::appendDatabaseCallback", e.getMessage());
+            Log.e(LOG_TAG, "urlString=" + urlString + " databaseIdentifier=" + databaseIdentifier,
+                    e);
         }
 
         output.append("UI DELEGATE DATABASE CALLBACK: ");
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
index cf82d24..bcc8564 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
@@ -124,9 +124,10 @@
                 }
             }
         } catch (FileNotFoundException e) {
-            Log.w(LOG_TAG + "::reloadConfiguration", "File not found: " + txt_exp.getPath());
+            Log.w(LOG_TAG, "mRootDirPath=" + mRootDirPath + ": File not found: " +
+                    txt_exp.getPath(), e);
         } catch (IOException e) {
-            Log.e(LOG_TAG + "::reloadConfiguration", "IOException: " + e.getMessage());
+            Log.e(LOG_TAG, "mRootDirPath=" + mRootDirPath, e);
         }
     }
 
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
index 212c187..0cfe69d 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
@@ -31,13 +31,13 @@
     public static final String LOG_TAG = "FsUtils";
 
     public static void writeDataToStorage(File file, byte[] bytes, boolean append) {
-        Log.d(LOG_TAG + "::writeDataToStorage", file.getAbsolutePath());
+        Log.d(LOG_TAG, "writeDataToStorage(): " + file.getAbsolutePath());
         try {
             OutputStream outputStream = null;
             try {
                 file.getParentFile().mkdirs();
                 file.createNewFile();
-                Log.d(LOG_TAG + "::writeDataToStorage", "File created.");
+                Log.d(LOG_TAG, "writeDataToStorage(): File created.");
                 outputStream = new FileOutputStream(file, append);
                 outputStream.write(bytes);
             } finally {
@@ -46,13 +46,13 @@
                 }
             }
         } catch (IOException e) {
-            Log.e(LOG_TAG + "::writeDataToStorage", e.getMessage());
+            Log.e(LOG_TAG, "file.getAbsolutePath=" + file.getAbsolutePath(), e);
         }
     }
 
     public static byte[] readDataFromStorage(File file) {
         if (!file.exists()) {
-            Log.d(LOG_TAG + "::readDataFromStorage", "File does not exist: "
+            Log.d(LOG_TAG, "readDataFromStorage(): File does not exist: "
                     + file.getAbsolutePath());
             return null;
         }
@@ -70,7 +70,7 @@
                 }
             }
         } catch (IOException e) {
-            Log.e(LOG_TAG + "::readDataFromStorage", e.getMessage());
+            Log.e(LOG_TAG, "file.getAbsolutePath=" + file.getAbsolutePath(), e);
         }
 
         return bytes;
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
index 6db9571..7d57eb7 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
@@ -56,7 +56,7 @@
     }
 
     public void clearAllDatabases() {
-        Log.w(LOG_TAG + "::clearAllDatabases", "called");
+        Log.i(LOG_TAG, "clearAllDatabases() called");
         WebStorage.getInstance().deleteAllData();
     }
 
@@ -70,7 +70,7 @@
 
     public void setDatabaseQuota(long quota) {
         /** TODO: Reset this before every test! */
-        Log.w(LOG_TAG + "::setDatabaseQuota", "called with: " + quota);
+        Log.i(LOG_TAG, "setDatabaseQuota() called with: " + quota);
         WebStorage.getInstance().setQuotaForOrigin(Uri.fromFile(new File("")).toString(),
                 quota);
     }
@@ -80,13 +80,13 @@
     }
 
     public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
-        Log.w(LOG_TAG + "::setMockGeolocationPosition", "latitude: " + latitude +
-                " longitude: " + longitude + " accuracy: " + accuracy);
+        Log.i(LOG_TAG, "setMockGeolocationPosition(): " + "latitude=" + latitude +
+                " longitude=" + longitude + " accuracy=" + accuracy);
         MockGeolocation.getInstance().setPosition(latitude, longitude, accuracy);
     }
 
     public void setMockGeolocationError(int code, String message) {
-        Log.w(LOG_TAG + "::setMockGeolocationError", "code: " + code + " message: " + message);
+        Log.i(LOG_TAG, "setMockGeolocationError(): " + "code=" + code + " message=" + message);
         MockGeolocation.getInstance().setError(code, message);
     }
 
@@ -94,7 +94,10 @@
             boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
         // Configuration is in WebKit, so stay on WebCore thread, but go via LayoutTestsExecutor
         // as we need access to the Webview.
+        Log.i(LOG_TAG, "setMockDeviceOrientation(" + canProvideAlpha +
+                ", " + alpha + ", " + canProvideBeta + ", " + beta + ", " + canProvideGamma +
+                ", " + gamma + ")");
         mLayoutTestsExecutor.setMockDeviceOrientation(
                 canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
     }
-}
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
index 4737657..d17ec7c 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -112,7 +112,7 @@
     private boolean mDumpDatabaseCallbacks;
     private boolean mIsGeolocationPermissionSet;
     private boolean mGeolocationPermission;
-    private Map mPendingGeolocationPermissionCallbacks;
+    private Map<GeolocationPermissions.Callback, String> mPendingGeolocationPermissionCallbacks;
 
     private EventSender mEventSender = new EventSender();
 
@@ -338,7 +338,7 @@
             serviceMsg.setData(bundle);
             mManagerServiceMessenger.send(serviceMsg);
         } catch (RemoteException e) {
-            Log.e(LOG_TAG + "::startTests", e.getMessage());
+            Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e);
         }
 
         runNextTest();
@@ -369,6 +369,7 @@
     private void onTestTimedOut() {
         assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
 
+        Log.w(LOG_TAG, "onTestTimedOut(): " + mCurrentTestRelativePath);
         mCurrentTestTimedOut = true;
 
         /**
@@ -383,6 +384,7 @@
     private void onTestFinished() {
         assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
 
+        Log.i(LOG_TAG, "onTestFinished(): " + mCurrentTestRelativePath);
         obtainActualResultsFromWebView();
     }
 
@@ -406,6 +408,7 @@
         assert mCurrentState == CurrentState.OBTAINING_RESULT
                 : "mCurrentState = " + mCurrentState.name();
 
+        Log.i(LOG_TAG, "onActualResultsObtained(): " + mCurrentTestRelativePath);
         mCurrentState = CurrentState.IDLE;
 
         mResultHandler.removeMessages(MSG_TEST_TIMED_OUT);
@@ -436,7 +439,7 @@
             serviceMsg.setData(bundle);
             mManagerServiceMessenger.send(serviceMsg);
         } catch (RemoteException e) {
-            Log.e(LOG_TAG + "::reportResultToService", e.getMessage());
+            Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e);
         }
     }
 
@@ -455,7 +458,7 @@
                     Message.obtain(null, ManagerService.MSG_ALL_TESTS_FINISHED);
             mManagerServiceMessenger.send(serviceMsg);
         } catch (RemoteException e) {
-            Log.e(LOG_TAG + "::onAllTestsFinished", e.getMessage());
+            Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e);
         }
 
         unbindService(mServiceConnection);
@@ -529,11 +532,11 @@
                     mGeolocationPermission = msg.arg1 == 1;
 
                     if (mPendingGeolocationPermissionCallbacks != null) {
-                        Iterator iter = mPendingGeolocationPermissionCallbacks.keySet().iterator();
+                        Iterator<GeolocationPermissions.Callback> iter =
+                                mPendingGeolocationPermissionCallbacks.keySet().iterator();
                         while (iter.hasNext()) {
-                            GeolocationPermissions.Callback callback =
-                                    (GeolocationPermissions.Callback) iter.next();
-                            String origin = (String) mPendingGeolocationPermissionCallbacks.get(callback);
+                            GeolocationPermissions.Callback callback = iter.next();
+                            String origin = mPendingGeolocationPermissionCallbacks.get(callback);
                             callback.invoke(origin, mGeolocationPermission, false);
                         }
                         mPendingGeolocationPermissionCallbacks = null;
@@ -541,7 +544,7 @@
                     break;
 
                 default:
-                    Log.w(LOG_TAG + "::handleMessage", "Message code does not exist: " + msg.what);
+                    assert false : "msg.what=" + msg.what;
                     break;
             }
         }
@@ -555,41 +558,42 @@
     }
 
     public void waitUntilDone() {
-        Log.w(LOG_TAG + "::waitUntilDone", "called");
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": waitUntilDone() called");
         mLayoutTestControllerHandler.sendEmptyMessage(MSG_WAIT_UNTIL_DONE);
     }
 
     public void notifyDone() {
-        Log.w(LOG_TAG + "::notifyDone", "called");
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": notifyDone() called");
         mLayoutTestControllerHandler.sendEmptyMessage(MSG_NOTIFY_DONE);
     }
 
     public void dumpAsText(boolean enablePixelTest) {
-        Log.w(LOG_TAG + "::dumpAsText(" + enablePixelTest + ")", "called");
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpAsText(" + enablePixelTest + ") called");
         /** TODO: Implement */
         if (enablePixelTest) {
-            Log.w(LOG_TAG + "::dumpAsText", "enablePixelTest not implemented, switching to false");
+            Log.w(LOG_TAG, "enablePixelTest not implemented, switching to false");
         }
         mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_AS_TEXT);
     }
 
     public void dumpChildFramesAsText() {
-        Log.w(LOG_TAG + "::dumpChildFramesAsText", "called");
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpChildFramesAsText() called");
         mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_CHILD_FRAMES_AS_TEXT);
     }
 
     public void setCanOpenWindows() {
-        Log.w(LOG_TAG + "::setCanOpenWindows", "called");
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setCanOpenWindows() called");
         mLayoutTestControllerHandler.sendEmptyMessage(MSG_SET_CAN_OPEN_WINDOWS);
     }
 
     public void dumpDatabaseCallbacks() {
-        Log.w(LOG_TAG + "::dumpDatabaseCallbacks:", "called");
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpDatabaseCallbacks() called");
         mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_DATABASE_CALLBACKS);
     }
 
     public void setGeolocationPermission(boolean allow) {
-        Log.w(LOG_TAG + "::setGeolocationPermission", "called");
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setGeolocationPermission(" + allow +
+                ") called");
         Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_GEOLOCATION_PERMISSION);
         msg.arg1 = allow ? 1 : 0;
         msg.sendToTarget();
@@ -597,7 +601,10 @@
 
     public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
             boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setMockDeviceOrientation(" + canProvideAlpha +
+                ", " + alpha + ", " + canProvideBeta + ", " + beta + ", " + canProvideGamma +
+                ", " + gamma + ")");
         mCurrentWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
                 canProvideGamma, gamma);
     }
-}
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
index 31026d6..951126f 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
@@ -88,7 +88,7 @@
                     break;
 
                 case MSG_PROCESS_ACTUAL_RESULTS:
-                    Log.d(LOG_TAG + ".mIncomingHandler", msg.getData().getString("relativePath"));
+                    Log.d(LOG_TAG,"mIncomingHandler: " + msg.getData().getString("relativePath"));
                     onActualResultsObtained(msg.getData());
                     break;
 
@@ -151,11 +151,13 @@
         AbstractResult results =
                 AbstractResult.TestType.valueOf(bundle.getString("type")).createResult(bundle);
 
+        Log.i(LOG_TAG, "onActualResultObtained: " + results.getRelativePath());
         handleResults(results);
     }
 
     private void ensureNextTestSetup(String nextTest, int index) {
         if (nextTest == null) {
+            Log.w(LOG_TAG, "ensureNextTestSetup(): nextTest=null");
             return;
         }
 
@@ -172,8 +174,8 @@
     private void onTestCrashed() {
         handleResults(new CrashedDummyResult(mCurrentlyRunningTest));
 
-        Log.w(LOG_TAG + "::onTestCrashed", mCurrentlyRunningTest +
-                "(" + mCurrentlyRunningTestIndex + ")");
+        Log.w(LOG_TAG, "onTestCrashed(): " + mCurrentlyRunningTest +
+                " (" + mCurrentlyRunningTestIndex + ")");
 
         Intent intent = new Intent(this, TestsListActivity.class);
         intent.setAction(Intent.ACTION_REBOOT);
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java
index 250b6bc..26bf75c 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java
@@ -204,4 +204,4 @@
         }
         return html.toString();
     }
-}
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java
new file mode 100644
index 0000000..d165a1a
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.forwarder;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * The utility class that can setup a socket allowing the device to communicate with remote
+ * machines through the machine that the device is connected to via adb.
+ */
+public class AdbUtils {
+    private static final String LOG_TAG = "AdbUtils";
+
+    private static final String ADB_OK = "OKAY";
+    private static final int ADB_PORT = 5037;
+    private static final String ADB_HOST = "127.0.0.1";
+    private static final int ADB_RESPONSE_SIZE = 4;
+
+    /**
+     * Send an ADB command using existing socket connection
+     *
+     * The streams provided must be from a socket connected to adb 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
+     */
+    private 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(LOG_TAG, "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 getSocketToRemoteMachine(String remoteAddress, int remotePort) {
+        try {
+            Socket socket = new Socket(ADB_HOST, ADB_PORT);
+            String cmd = "tcp:" + remotePort + ":" + remoteAddress;
+            if (!sendAdbCmd(socket.getInputStream(), socket.getOutputStream(), cmd)) {
+                socket.close();
+                return null;
+            }
+            return socket;
+        } catch (IOException ioe) {
+            Log.w(LOG_TAG, "error creating adb socket", ioe);
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java
new file mode 100644
index 0000000..6a6cb7f
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.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 Forwarder}. A ConnectionHandler will be created once the Forwarder
+ * accepts an incoming connection, and it will then forward the incoming/outgoing streams to a
+ * connection already proxied by adb networking (see also {@link AdbUtils}).
+ */
+public class ConnectionHandler {
+
+    private static final String LOG_TAG = "ConnectionHandler";
+
+    private class SocketPipeThread extends Thread {
+
+        private Socket mInSocket, mOutSocket;
+
+        public SocketPipeThread(Socket inSocket, Socket outSocket) {
+            mInSocket = inSocket;
+            mOutSocket = outSocket;
+        }
+
+        @Override
+        public void run() {
+            InputStream is;
+            OutputStream os;
+            try {
+                synchronized (this) {
+                    is = mInSocket.getInputStream();
+                    os = mOutSocket.getOutputStream();
+                }
+            } catch (IOException e) {
+                Log.w(LOG_TAG, this.toString(), e);
+                return;
+            }
+
+            byte[] buffer = new byte[4096];
+            int length;
+            while (true) {
+                try {
+                    synchronized (this) {
+                        if ((length = is.read(buffer)) <= 0) {
+                            break;
+                        }
+                        os.write(buffer, 0, length);
+                    }
+                } catch (IOException e) {
+                    /** This exception means one of the streams is closed */
+                    Log.v(LOG_TAG, this.toString(), e);
+                    break;
+                }
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "SocketPipeThread:\n" + mInSocket + "\n=>\n" + mOutSocket;
+        }
+    }
+
+    private Socket mFromSocket, mToSocket;
+    private SocketPipeThread mFromToPipe, mToFromPipe;
+
+    public ConnectionHandler(Socket fromSocket, Socket toSocket) {
+        mFromSocket = fromSocket;
+        mToSocket = toSocket;
+        mFromToPipe = new SocketPipeThread(mFromSocket, mToSocket);
+        mToFromPipe = new SocketPipeThread(mToSocket, mFromSocket);
+    }
+
+    public void start() {
+        mFromToPipe.start();
+        mToFromPipe.start();
+    }
+
+    public void stop() {
+        shutdown(mFromSocket);
+        shutdown(mToSocket);
+    }
+
+    private void shutdown(Socket socket) {
+        try {
+            synchronized (mFromToPipe) {
+                synchronized (mToFromPipe) {
+                    /** This will stop the while loop in the run method */
+                    socket.shutdownInput();
+                    socket.shutdownOutput();
+                    socket.close();
+                }
+            }
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java
new file mode 100644
index 0000000..e5ef6da
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.forwarder;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A port forwarding server. Listens on localhost on specified port and forwards the tcp
+ * communications to external socket via adb networking proxy.
+ */
+public class Forwarder extends Thread {
+    private static final String LOG_TAG = "Forwarder";
+
+    private int mPort;
+    private String mRemoteMachineIpAddress;
+
+    private Boolean mIsRunning = false;
+    private ServerSocket mServerSocket;
+
+    private Set<ConnectionHandler> mConnectionHandlers = new HashSet<ConnectionHandler>();
+
+    public Forwarder(int port, String remoteMachineIpAddress) {
+        mPort = port;
+        mRemoteMachineIpAddress = remoteMachineIpAddress;
+    }
+
+    @Override
+    public void start() {
+        Log.i(LOG_TAG, "start(): Starting fowarder on port: " + mPort);
+        synchronized (this) {
+            if (mIsRunning) {
+                Log.w(LOG_TAG, "start(): Forwarder on port: " + mPort + " already running! NOOP.");
+                return;
+            }
+        }
+
+        try {
+            mServerSocket = new ServerSocket(mPort);
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "mPort=" + mPort, e);
+            return;
+        }
+
+        mIsRunning = true;
+        super.start();
+    }
+
+    @Override
+    public void run() {
+        while (true) {
+            synchronized (this) {
+                if (!mIsRunning) {
+                    return;
+                }
+
+                /** These sockets will be closed when Forwarder.stop() is called */
+                Socket localSocket;
+                Socket remoteSocket;
+                try {
+                    localSocket = mServerSocket.accept();
+                    remoteSocket = AdbUtils.getSocketToRemoteMachine(mRemoteMachineIpAddress,
+                            mPort);
+                } catch (IOException e) {
+                    /** This most likely means that mServerSocket is already closed */
+                    Log.w(LOG_TAG + "mPort=" + mPort, e);
+                    return;
+                }
+
+                if (remoteSocket == null) {
+                    try {
+                        localSocket.close();
+                    } catch (IOException e) {
+                        Log.e(LOG_TAG, "mPort=" + mPort, e);
+                    }
+
+                    Log.e(LOG_TAG, "run(): mPort= " + mPort + " Failed to start forwarding from " +
+                            localSocket);
+                    continue;
+                }
+
+                ConnectionHandler forwarder = new ConnectionHandler(localSocket, remoteSocket);
+                mConnectionHandlers.add(forwarder);
+                forwarder.start();
+
+            }
+        }
+    }
+
+    public void finish() {
+        synchronized (this) {
+            if (!mIsRunning) {
+                return;
+            }
+        }
+
+        try {
+            mServerSocket.close();
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "mPort=" + mPort, e);
+        }
+
+        synchronized (this) {
+            mIsRunning = false;
+        }
+
+        for (ConnectionHandler connectionHandler : mConnectionHandlers) {
+            connectionHandler.stop();
+        }
+        mConnectionHandlers.clear();
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java
new file mode 100644
index 0000000..10fee4b
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.forwarder;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A simple class to start and stop Forwarders running on some ports.
+ *
+ * It uses a singleton pattern and is thread safe.
+ */
+public class ForwarderManager {
+    /**
+     * The IP address of the server serving the tests.
+     */
+    private static final String HOST_IP = "127.0.0.1";
+
+    /**
+     * We use these ports because other webkit platforms do. They are set up in
+     * external/webkit/LayoutTests/http/conf/apache2-debian-httpd.conf
+     */
+    public static final int HTTP_PORT = 8080;
+    public static final int HTTPS_PORT = 8443;
+
+    private static ForwarderManager forwarderManager;
+
+    private Set<Forwarder> mServers;
+
+    private ForwarderManager() {
+        mServers = new HashSet<Forwarder>(2);
+        mServers.add(new Forwarder(HTTP_PORT, HOST_IP));
+        mServers.add(new Forwarder(HTTPS_PORT, HOST_IP));
+    }
+
+    public static synchronized ForwarderManager getForwarderManager() {
+        if (forwarderManager == null) {
+            forwarderManager = new ForwarderManager();
+        }
+        return forwarderManager;
+    }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+
+    public synchronized void start() {
+        for (Forwarder server : mServers) {
+            server.start();
+        }
+    }
+
+    public synchronized void stop() {
+        for (Forwarder server : mServers) {
+            server.finish();
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
index ddfae69..e730c8a 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
@@ -66,7 +66,7 @@
                 try {
                     this.wait();
                 } catch (InterruptedException e) {
-                    Log.e(LOG_TAG + "::startLayoutTests", e.getMessage());
+                    Log.e(LOG_TAG, "startLayoutTests()", e);
                 }
             }
         }
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index abccf9b..b50a393 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -141,9 +141,9 @@
     if (bundle->getVerbose()) {
         printf("Archive:  %s\n", zipFileName);
         printf(
-            " Length   Method    Size  Ratio   Date   Time   CRC-32    Name\n");
+            " Length   Method    Size  Ratio   Offset      Date  Time  CRC-32    Name\n");
         printf(
-            "--------  ------  ------- -----   ----   ----   ------    ----\n");
+            "--------  ------  ------- -----  -------      ----  ----  ------    ----\n");
     }
 
     totalUncLen = totalCompLen = 0;
@@ -159,12 +159,13 @@
             strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
                 localtime(&when));
 
-            printf("%8ld  %-7.7s %7ld %3d%%  %s  %08lx  %s\n",
+            printf("%8ld  %-7.7s %7ld %3d%%  %8zd  %s  %08lx  %s\n",
                 (long) entry->getUncompressedLen(),
                 compressionName(entry->getCompressionMethod()),
                 (long) entry->getCompressedLen(),
                 calcPercent(entry->getUncompressedLen(),
                             entry->getCompressedLen()),
+                (size_t) entry->getLFHOffset(),
                 dateBuf,
                 entry->getCRC32(),
                 entry->getFileName());
diff --git a/tools/aapt/ZipEntry.h b/tools/aapt/ZipEntry.h
index 7f721b4..c2f3227 100644
--- a/tools/aapt/ZipEntry.h
+++ b/tools/aapt/ZipEntry.h
@@ -72,6 +72,11 @@
     off_t getCompressedLen(void) const { return mCDE.mCompressedSize; }
 
     /*
+     * Return the offset of the local file header.
+     */
+    off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; }
+
+    /*
      * Return the absolute file offset of the start of the compressed or
      * uncompressed data.
      */
@@ -186,11 +191,6 @@
     void setModWhen(time_t when);
 
     /*
-     * Return the offset of the local file header.
-     */
-    off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; }
-
-    /*
      * Set the offset of the local file header, relative to the start of
      * the current file.
      */
diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java
index e71c293..6c99141 100644
--- a/voip/java/android/net/sip/SipProfile.java
+++ b/voip/java/android/net/sip/SipProfile.java
@@ -35,7 +35,7 @@
  * Class containing a SIP account, domain and server information.
  * @hide
  */
-public class SipProfile implements Parcelable, Serializable {
+public class SipProfile implements Parcelable, Serializable, Cloneable {
     private static final long serialVersionUID = 1L;
     private static final int DEFAULT_PORT = 5060;
     private Address mAddress;
@@ -46,6 +46,8 @@
     private String mProfileName;
     private boolean mSendKeepAlive = false;
     private boolean mAutoRegistration = true;
+    private boolean mAllowOutgoingCall = false;
+    private int mCallingUid = -1;
 
     /** @hide */
     public static final Parcelable.Creator<SipProfile> CREATOR =
@@ -79,6 +81,23 @@
         }
 
         /**
+         * Creates a builder based on the given profile.
+         */
+        public Builder(SipProfile profile) {
+            if (profile == null) throw new NullPointerException();
+            try {
+                mProfile = (SipProfile) profile.clone();
+            } catch (CloneNotSupportedException e) {
+                throw new RuntimeException("should not occur", e);
+            }
+            mProfile.mAddress = null;
+            mUri = profile.getUri();
+            mUri.setUserPassword(profile.getPassword());
+            mDisplayName = profile.getDisplayName();
+            mProxyAddress = profile.getProxyAddress();
+        }
+
+        /**
          * Constructor.
          *
          * @param uriString the URI string as "sip:<user_name>@<domain>"
@@ -226,6 +245,18 @@
         }
 
         /**
+         * Sets the allow-outgoing-call flag.
+         *
+         * @param flag true if allowing to make outgoing call on the profile;
+         *      false otherwise
+         * @return this builder object
+         */
+        public Builder setOutgoingCallAllowed(boolean flag) {
+            mProfile.mAllowOutgoingCall = flag;
+            return this;
+        }
+
+        /**
          * Builds and returns the SIP profile object.
          *
          * @return the profile object created
@@ -262,6 +293,8 @@
         mProfileName = in.readString();
         mSendKeepAlive = (in.readInt() == 0) ? false : true;
         mAutoRegistration = (in.readInt() == 0) ? false : true;
+        mAllowOutgoingCall = (in.readInt() == 0) ? false : true;
+        mCallingUid = in.readInt();
     }
 
     /** @hide */
@@ -274,6 +307,8 @@
         out.writeString(mProfileName);
         out.writeInt(mSendKeepAlive ? 1 : 0);
         out.writeInt(mAutoRegistration ? 1 : 0);
+        out.writeInt(mAllowOutgoingCall ? 1 : 0);
+        out.writeInt(mCallingUid);
     }
 
     /** @hide */
@@ -398,4 +433,27 @@
     public boolean getAutoRegistration() {
         return mAutoRegistration;
     }
+
+    /**
+     * Returns true if allowing to make outgoing calls on this profile.
+     */
+    public boolean isOutgoingCallAllowed() {
+        return mAllowOutgoingCall;
+    }
+
+    /**
+     * Sets the calling process's Uid in the sip service.
+     * @hide
+     */
+    public void setCallingUid(int uid) {
+        mCallingUid = uid;
+    }
+
+    /**
+     * Gets the calling process's Uid in the sip settings.
+     * @hide
+     */
+    public int getCallingUid() {
+        return mCallingUid;
+    }
 }
diff --git a/voip/jni/rtp/Android.mk b/voip/jni/rtp/Android.mk
index 3bd85aa..a364355 100644
--- a/voip/jni/rtp/Android.mk
+++ b/voip/jni/rtp/Android.mk
@@ -32,11 +32,10 @@
 	libutils \
 	libmedia
 
-LOCAL_STATIC_LIBRARIES := libspeex
+LOCAL_STATIC_LIBRARIES :=
 
 LOCAL_C_INCLUDES += \
-	$(JNI_H_INCLUDE) \
-	external/speex/include
+	$(JNI_H_INCLUDE)
 
 LOCAL_CFLAGS += -fvisibility=hidden
 
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index b5a4e22..bb45a9a 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -40,8 +40,6 @@
 #include <media/AudioTrack.h>
 #include <media/mediarecorder.h>
 
-#include <speex/speex_echo.h>
-
 #include "jni.h"
 #include "JNIHelp.h"
 
@@ -447,8 +445,6 @@
     int mDeviceSocket;
     AudioTrack mTrack;
     AudioRecord mRecord;
-    
-    SpeexEchoState *mEchoState;
 
     bool networkLoop();
     bool deviceLoop();
@@ -510,7 +506,6 @@
     mEventQueue = -1;
     mDtmfEvent = -1;
     mDeviceSocket = -1;
-    mEchoState = NULL;
     mNetworkThread = new NetworkThread(this);
     mDeviceThread = new DeviceThread(this);
 }
@@ -523,9 +518,6 @@
     mRecord.stop();
     close(mEventQueue);
     close(mDeviceSocket);
-    if (mEchoState) {
-        speex_echo_state_destroy(mEchoState);
-    }
     while (mChain) {
         AudioStream *next = mChain->mNext;
         delete mChain;
@@ -574,8 +566,7 @@
     }
     LOGD("latency: output %d, input %d", mTrack.latency(), mRecord.latency());
 
-    // Initialize echo canceller.
-    mEchoState = speex_echo_state_init(sampleCount, sampleRate);
+    // TODO: initialize echo canceler here.
 
     // Create device socket.
     int pair[2];
@@ -642,7 +633,6 @@
     if (mode == MUTED) {
         mRecord.stop();
     } else {
-        speex_echo_state_reset(mEchoState);
         mRecord.start();
     }
 
@@ -803,7 +793,7 @@
 
             status_t status = mRecord.obtainBuffer(&buffer, 1);
             if (status == NO_ERROR) {
-                int count = ((int)buffer.frameCount < toRead) ?
+                int count = (buffer.frameCount < toRead) ?
                         buffer.frameCount : toRead;
                 memcpy(&input[mSampleCount - toRead], buffer.i8, count * 2);
                 toRead -= count;
@@ -827,9 +817,8 @@
         if (mMode == NORMAL) {
             send(mDeviceSocket, input, sizeof(input), MSG_DONTWAIT);
         } else {
-            int16_t result[mSampleCount];
-            speex_echo_cancellation(mEchoState, input, output, result);
-            send(mDeviceSocket, result, sizeof(result), MSG_DONTWAIT);
+            // TODO: Echo canceller runs here.
+            send(mDeviceSocket, input, sizeof(input), MSG_DONTWAIT);
         }
     }
     return true;
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 5734953..492f69c 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -736,7 +736,11 @@
     }
 
     public void connectNetwork(WifiConfiguration wifiConfig) {
-        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, wifiConfig));
+        /* arg1 is used to indicate netId, force a netId value of -1 when
+         * we are passing a configuration since the default value of
+         * 0 is a valid netId
+         */
+        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, -1, 0, wifiConfig));
     }
 
     public void saveNetwork(WifiConfiguration wifiConfig) {
@@ -1469,16 +1473,13 @@
     }
 
     private void enableAllNetworks() {
-        if (mEnableAllNetworks) {
-            mEnableAllNetworks = false;
-            for (WifiConfiguration config : mConfiguredNetworks) {
-                if(config != null && config.status == Status.DISABLED) {
-                    WifiNative.enableNetworkCommand(config.networkId, false);
-                }
+        for (WifiConfiguration config : mConfiguredNetworks) {
+            if(config != null && config.status == Status.DISABLED) {
+                WifiNative.enableNetworkCommand(config.networkId, false);
             }
-            WifiNative.saveConfigCommand();
-            updateConfigAndSendChangeBroadcast();
         }
+        WifiNative.saveConfigCommand();
+        updateConfigAndSendChangeBroadcast();
     }
 
     private int addOrUpdateNetworkNative(WifiConfiguration config) {
@@ -2404,7 +2405,8 @@
 
                     mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
 
-                    updateConfigAndSendChangeBroadcast();
+                    updateConfiguredNetworks();
+                    enableAllNetworks();
 
                     //TODO: initialize and fix multicast filtering
                     //mWM.initializeMulticastFiltering();
@@ -2875,15 +2877,25 @@
                     addOrUpdateNetworkNative(config);
                     WifiNative.saveConfigCommand();
 
-                    // Connect to network by disabling others.
+                    /* We connect to a specific network by first enabling that network
+                     * and disabling all other networks in the supplicant. Disabling a
+                     * connected network will cause a disconnection from the network.
+                     * A reconnectCommand() will then initiate a connection to the enabled
+                     * network.
+                     */
                     WifiNative.enableNetworkCommand(netId, true);
-                    WifiNative.reconnectCommand();
+                    /* Save a flag to indicate that we need to enable all
+                     * networks after supplicant indicates a network
+                     * state change event
+                     */
                     mEnableAllNetworks = true;
+                    WifiNative.reconnectCommand();
                     /* update the configured networks but not send a
                      * broadcast to avoid a fetch from settings
                      * during this temporary disabling of networks
                      */
                     updateConfiguredNetworks();
+                    transitionTo(mDisconnectingState);
                     break;
                 case SCAN_RESULTS_EVENT:
                     /* Set the scan setting back to "connect" mode */
@@ -2899,16 +2911,13 @@
                     mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
                     mWifiInfo.setNetworkId(stateChangeResult.networkId);
                     mLastNetworkId = stateChangeResult.networkId;
-                    enableAllNetworks();
                     /* send event to CM & network change broadcast */
                     setDetailedState(DetailedState.OBTAINING_IPADDR);
                     sendNetworkStateChangeBroadcast(mLastBssid);
-
                     transitionTo(mConnectingState);
                     break;
                 case NETWORK_DISCONNECTION_EVENT:
                     Log.d(TAG,"Network connection lost");
-                    enableAllNetworks();
                     handleNetworkDisconnect();
                     transitionTo(mDisconnectedState);
                     break;
@@ -3057,6 +3066,13 @@
                   WifiNative.disconnectCommand();
                   transitionTo(mDisconnectingState);
                   break;
+                  /* Ignore connection to same network */
+              case CMD_CONNECT_NETWORK:
+                  int netId = message.arg1;
+                  if (mWifiInfo.getNetworkId() == netId) {
+                      break;
+                  }
+                  return NOT_HANDLED;
                   /* Ignore */
               case NETWORK_CONNECTION_EVENT:
                   break;
@@ -3127,6 +3143,13 @@
                         deferMessage(message);
                     }
                     break;
+                    /* Ignore connection to same network */
+                case CMD_CONNECT_NETWORK:
+                    int netId = message.arg1;
+                    if (mWifiInfo.getNetworkId() == netId) {
+                        break;
+                    }
+                    return NOT_HANDLED;
                     /* Ignore */
                 case NETWORK_CONNECTION_EVENT:
                     break;
@@ -3162,6 +3185,13 @@
             EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
+        @Override
+        public void exit() {
+            if (mEnableAllNetworks) {
+                mEnableAllNetworks = false;
+                enableAllNetworks();
+            }
+        }
     }
 
     class DisconnectedState extends HierarchicalState {