Merge "Turn off the spell checker when the spell checker is disabled in the settings" into ics-mr1
diff --git a/api/current.txt b/api/current.txt
index 9d38566..808cb6f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14399,6 +14399,7 @@
     field public static final int HONEYCOMB_MR1 = 12; // 0xc
     field public static final int HONEYCOMB_MR2 = 13; // 0xd
     field public static final int ICE_CREAM_SANDWICH = 14; // 0xe
+    field public static final int ICE_CREAM_SANDWICH_MR1 = 15; // 0xf
   }
 
   public final class Bundle implements java.lang.Cloneable android.os.Parcelable {
@@ -18636,6 +18637,7 @@
     method public android.os.Bundle getBundle();
     method public java.lang.String getLocale();
     method public void onCancel();
+    method public void onClose();
     method public abstract void onCreate();
     method public abstract android.view.textservice.SuggestionsInfo onGetSuggestions(android.view.textservice.TextInfo, int);
     method public android.view.textservice.SuggestionsInfo[] onGetSuggestionsMultiple(android.view.textservice.TextInfo[], int, boolean);
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 72e63836..f1ce2bb 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -28,6 +28,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.util.Log;
 import android.util.TypedValue;
 import android.view.ActionMode;
 import android.view.ContextMenu;
@@ -77,6 +78,7 @@
  */
 public class Dialog implements DialogInterface, Window.Callback,
         KeyEvent.Callback, OnCreateContextMenuListener {
+    private static final String TAG = "Dialog";
     private Activity mOwnerActivity;
     
     final Context mContext;
@@ -300,15 +302,21 @@
         if (Thread.currentThread() != mUiThread) {
             mHandler.post(mDismissAction);
         } else {
+            mHandler.removeCallbacks(mDismissAction);
             mDismissAction.run();
         }
     }
 
-    private void dismissDialog() {
+    void dismissDialog() {
         if (mDecor == null || !mShowing) {
             return;
         }
 
+        if (mWindow.isDestroyed()) {
+            Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
+            return;
+        }
+
         try {
             mWindowManager.removeView(mDecor);
         } finally {
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 85aec4c..c4ba778 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -725,8 +725,9 @@
             // While removing a fragment, we can't change it to a higher state.
             newState = f.mState;
         }
-        // Defer start if requested; don't allow it to move to STARTED or higher.
-        if (f.mDeferStart && newState > Fragment.STOPPED) {
+        // Defer start if requested; don't allow it to move to STARTED or higher
+        // if it's not already started.
+        if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
             newState = Fragment.STOPPED;
         }
         if (f.mState < newState) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3eb7647..8541748d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -153,10 +153,14 @@
     public static final int GET_PERMISSIONS               = 0x00001000;
 
     /**
-     * Flag parameter to retrieve all applications(even uninstalled ones) with data directories.
-     * This state could have resulted if applications have been deleted with flag
-     * DONT_DELETE_DATA
-     * with a possibility of being replaced or reinstalled in future
+     * Flag parameter to retrieve some information about all applications (even
+     * uninstalled ones) which have data directories. This state could have
+     * resulted if applications have been deleted with flag
+     * {@code DONT_DELETE_DATA} with a possibility of being replaced or
+     * reinstalled in future.
+     * <p>
+     * Note: this flag may cause less information about currently installed
+     * applications to be returned.
      */
     public static final int GET_UNINSTALLED_PACKAGES = 0x00002000;
 
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 5343e2a..5143f7f 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -1145,44 +1145,29 @@
 
     @Override
     public boolean onHoverEvent(MotionEvent event) {
-        // If touch exploring is enabled we ignore touch events and transform
-        // the stream of hover events as touch events. This allows one consistent
-        // event stream to drive the keyboard since during touch exploring the
-        // first touch generates only hover events and tapping on the same
-        // location generates hover and touch events.
         if (mAccessibilityManager.isTouchExplorationEnabled() && event.getPointerCount() == 1) {
             final int action = event.getAction();
             switch (action) {
                 case MotionEvent.ACTION_HOVER_ENTER:
-                    event.setAction(MotionEvent.ACTION_DOWN);
-                    break;
                 case MotionEvent.ACTION_HOVER_MOVE:
-                    event.setAction(MotionEvent.ACTION_MOVE);
+                    final int touchX = (int) event.getX() - mPaddingLeft;
+                    int touchY = (int) event.getY() - mPaddingTop;
+                    if (touchY >= -mVerticalCorrection) {
+                        touchY += mVerticalCorrection;
+                    }
+                    final int keyIndex = getKeyIndices(touchX, touchY, null);
+                    showPreview(keyIndex);
                     break;
                 case MotionEvent.ACTION_HOVER_EXIT:
-                    event.setAction(MotionEvent.ACTION_UP);
+                    showPreview(NOT_A_KEY);
                     break;
             }
-            onTouchEventInternal(event);
-            event.setAction(action);
         }
-        return super.onHoverEvent(event);
+        return true;
     }
 
     @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        // If touch exploring is enabled we ignore touch events and transform
-        // the stream of hover events as touch events. This allows one consistent
-        // event stream to drive the keyboard since during touch exploring the
-        // first touch generates only hover events and tapping on the same
-        // location generates hover and touch events.
-        if (mAccessibilityManager.isTouchExplorationEnabled()) {
-            return true;
-        }
-        return onTouchEventInternal(event);
-    }
-
-    private boolean onTouchEventInternal(MotionEvent me) {
+    public boolean onTouchEvent(MotionEvent me) {
         // Convert multi-pointer up/down events to single up/down events to 
         // deal with the typical multi-pointer behavior of two-thumb typing
         final int pointerCount = me.getPointerCount();
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 69ac1e7..5c6ef1a 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -16,10 +16,11 @@
 
 package android.net;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
-import android.util.Log;
 import android.util.SparseBooleanArray;
 
 import com.android.internal.util.Objects;
@@ -54,6 +55,8 @@
     /** {@link #tag} value for total data across all tags. */
     public static final int TAG_NONE = 0;
 
+    // TODO: move fields to "mVariable" notation
+
     /**
      * {@link SystemClock#elapsedRealtime()} timestamp when this data was
      * generated.
@@ -295,8 +298,33 @@
      */
     public int findIndex(String iface, int uid, int set, int tag) {
         for (int i = 0; i < size; i++) {
-            if (Objects.equal(iface, this.iface[i]) && uid == this.uid[i] && set == this.set[i]
-                    && tag == this.tag[i]) {
+            if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
+                    && Objects.equal(iface, this.iface[i])) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Find first stats index that matches the requested parameters, starting
+     * search around the hinted index as an optimization.
+     */
+    // @VisibleForTesting
+    public int findIndexHinted(String iface, int uid, int set, int tag, int hintIndex) {
+        for (int offset = 0; offset < size; offset++) {
+            final int halfOffset = offset / 2;
+
+            // search outwards from hint index, alternating forward and backward
+            final int i;
+            if (offset % 2 == 0) {
+                i = (hintIndex + halfOffset) % size;
+            } else {
+                i = (size + hintIndex - halfOffset - 1) % size;
+            }
+
+            if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
+                    && Objects.equal(iface, this.iface[i])) {
                 return i;
             }
         }
@@ -423,40 +451,10 @@
      * Subtract the given {@link NetworkStats}, effectively leaving the delta
      * between two snapshots in time. Assumes that statistics rows collect over
      * time, and that none of them have disappeared.
-     *
-     * @throws IllegalArgumentException when given {@link NetworkStats} is
-     *             non-monotonic.
      */
-    public NetworkStats subtract(NetworkStats value) {
-        return subtract(value, true, false);
-    }
-
-    /**
-     * Subtract the given {@link NetworkStats}, effectively leaving the delta
-     * between two snapshots in time. Assumes that statistics rows collect over
-     * time, and that none of them have disappeared.
-     * <p>
-     * Instead of throwing when counters are non-monotonic, this variant clamps
-     * results to never be negative.
-     */
-    public NetworkStats subtractClamped(NetworkStats value) {
-        return subtract(value, false, true);
-    }
-
-    /**
-     * Subtract the given {@link NetworkStats}, effectively leaving the delta
-     * between two snapshots in time. Assumes that statistics rows collect over
-     * time, and that none of them have disappeared.
-     *
-     * @param enforceMonotonic Validate that incoming value is strictly
-     *            monotonic compared to this object.
-     * @param clampNegative Instead of throwing like {@code enforceMonotonic},
-     *            clamp resulting counters at 0 to prevent negative values.
-     */
-    private NetworkStats subtract(
-            NetworkStats value, boolean enforceMonotonic, boolean clampNegative) {
+    public NetworkStats subtract(NetworkStats value) throws NonMonotonicException {
         final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime;
-        if (enforceMonotonic && deltaRealtime < 0) {
+        if (deltaRealtime < 0) {
             throw new IllegalArgumentException("found non-monotonic realtime");
         }
 
@@ -470,7 +468,7 @@
             entry.tag = tag[i];
 
             // find remote row that matches, and subtract
-            final int j = value.findIndex(entry.iface, entry.uid, entry.set, entry.tag);
+            final int j = value.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag, i);
             if (j == -1) {
                 // newly appearing row, return entire value
                 entry.rxBytes = rxBytes[i];
@@ -485,20 +483,10 @@
                 entry.txBytes = txBytes[i] - value.txBytes[j];
                 entry.txPackets = txPackets[i] - value.txPackets[j];
                 entry.operations = operations[i] - value.operations[j];
-                if (enforceMonotonic
-                        && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
-                                || entry.txPackets < 0 || entry.operations < 0)) {
-                    Log.v(TAG, "lhs=" + this);
-                    Log.v(TAG, "rhs=" + value);
-                    throw new IllegalArgumentException(
-                            "found non-monotonic values at lhs[" + i + "] - rhs[" + j + "]");
-                }
-                if (clampNegative) {
-                    entry.rxBytes = Math.max(0, entry.rxBytes);
-                    entry.rxPackets = Math.max(0, entry.rxPackets);
-                    entry.txBytes = Math.max(0, entry.txBytes);
-                    entry.txPackets = Math.max(0, entry.txPackets);
-                    entry.operations = Math.max(0, entry.operations);
+
+                if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
+                        || entry.txPackets < 0 || entry.operations < 0) {
+                    throw new NonMonotonicException(this, i, value, j);
                 }
             }
 
@@ -564,6 +552,24 @@
         return stats;
     }
 
+    /**
+     * Return all rows except those attributed to the requested UID; doesn't
+     * mutate the original structure.
+     */
+    public NetworkStats withoutUid(int uid) {
+        final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
+
+        Entry entry = new Entry();
+        for (int i = 0; i < size; i++) {
+            entry = getValues(i, entry);
+            if (entry.uid != uid) {
+                stats.addValues(entry);
+            }
+        }
+
+        return stats;
+    }
+
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix);
         pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
@@ -625,4 +631,19 @@
             return new NetworkStats[size];
         }
     };
+
+    public static class NonMonotonicException extends Exception {
+        public final NetworkStats left;
+        public final NetworkStats right;
+        public final int leftIndex;
+        public final int rightIndex;
+
+        public NonMonotonicException(
+                NetworkStats left, int leftIndex, NetworkStats right, int rightIndex) {
+            this.left = checkNotNull(left, "missing left");
+            this.right = checkNotNull(right, "missing right");
+            this.leftIndex = leftIndex;
+            this.rightIndex = rightIndex;
+        }
+    }
 }
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 18eb9f6..cd585b2 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -20,6 +20,7 @@
 import android.app.backup.BackupManager;
 import android.content.Context;
 import android.media.MediaPlayer;
+import android.net.NetworkStats.NonMonotonicException;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 
@@ -192,12 +193,15 @@
                 throw new IllegalStateException("not profiling data");
             }
 
-            // subtract starting values and return delta
-            final NetworkStats profilingStop = getDataLayerSnapshotForUid(context);
-            final NetworkStats profilingDelta = profilingStop.subtractClamped(
-                    sActiveProfilingStart);
-            sActiveProfilingStart = null;
-            return profilingDelta;
+            try {
+                // subtract starting values and return delta
+                final NetworkStats profilingStop = getDataLayerSnapshotForUid(context);
+                final NetworkStats profilingDelta = profilingStop.subtract(sActiveProfilingStart);
+                sActiveProfilingStart = null;
+                return profilingDelta;
+            } catch (NonMonotonicException e) {
+                throw new RuntimeException(e);
+            }
         }
     }
 
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 17a882d..7d034940 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -277,7 +277,7 @@
         public static final int HONEYCOMB_MR2 = 13;
 
         /**
-         * Android 4.0.
+         * October 2011: Android 4.0.
          *
          * <p>Applications targeting this or a later release will get these
          * new changes in behavior:</p>
@@ -309,6 +309,11 @@
          * </ul>
          */
         public static final int ICE_CREAM_SANDWICH = 14;
+
+        /**
+         * Android 4.1.
+         */
+        public static final int ICE_CREAM_SANDWICH_MR1 = 15;
     }
     
     /** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index cc2fa85..99f58a0 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -116,6 +116,14 @@
     private static final boolean IS_ENG_BUILD = "eng".equals(Build.TYPE);
 
     /**
+     * Boolean system property to disable strict mode checks outright.
+     * Set this to 'true' to force disable; 'false' has no effect on other
+     * enable/disable policy.
+     * @hide
+     */
+    public static final String DISABLE_PROPERTY = "persist.sys.strictmode.disable";
+
+    /**
      * The boolean system property to control screen flashes on violations.
      *
      * @hide
@@ -891,16 +899,24 @@
      * @hide
      */
     public static boolean conditionallyEnableDebugLogging() {
-        boolean doFlashes = !amTheSystemServerProcess() &&
-                SystemProperties.getBoolean(VISUAL_PROPERTY, IS_ENG_BUILD);
+        boolean doFlashes = SystemProperties.getBoolean(VISUAL_PROPERTY, false)
+                && !amTheSystemServerProcess();
+        final boolean suppress = SystemProperties.getBoolean(DISABLE_PROPERTY, false);
 
         // For debug builds, log event loop stalls to dropbox for analysis.
         // Similar logic also appears in ActivityThread.java for system apps.
-        if (IS_USER_BUILD && !doFlashes) {
+        if (!doFlashes && (IS_USER_BUILD || suppress)) {
             setCloseGuardEnabled(false);
             return false;
         }
 
+        // Eng builds have flashes on all the time.  The suppression property
+        // overrides this, so we force the behavior only after the short-circuit
+        // check above.
+        if (IS_ENG_BUILD) {
+            doFlashes = true;
+        }
+
         // Thread policy controls BlockGuard.
         int threadPolicyMask = StrictMode.DETECT_DISK_WRITE |
                 StrictMode.DETECT_DISK_READ |
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index 2ecf307..28251a6 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -146,6 +146,14 @@
         public void onCancel() {}
 
         /**
+         * Request to close this session.
+         * This function will run on the incoming IPC thread.
+         * So, this is not called on the main thread,
+         * but will be called in series on another thread.
+         */
+        public void onClose() {}
+
+        /**
          * @return Locale for this session
          */
         public String getLocale() {
@@ -162,7 +170,7 @@
 
     // Preventing from exposing ISpellCheckerSession.aidl, create an internal class.
     private static class InternalISpellCheckerSession extends ISpellCheckerSession.Stub {
-        private final ISpellCheckerSessionListener mListener;
+        private ISpellCheckerSessionListener mListener;
         private final Session mSession;
         private final String mLocale;
         private final Bundle mBundle;
@@ -192,6 +200,12 @@
             mSession.onCancel();
         }
 
+        @Override
+        public void onClose() {
+            mSession.onClose();
+            mListener = null;
+        }
+
         public String getLocale() {
             return mLocale;
         }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 70681ac..62e6ebd 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4171,10 +4171,12 @@
         bounds.offset(locationOnScreen[0], locationOnScreen[1]);
         info.setBoundsInScreen(bounds);
 
-        ViewParent parent = getParent();
-        if (parent instanceof View) {
-            View parentView = (View) parent;
-            info.setParent(parentView);
+        if ((mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
+            ViewParent parent = getParent();
+            if (parent instanceof View) {
+                View parentView = (View) parent;
+                info.setParent(parentView);
+            }
         }
 
         info.setPackageName(mContext.getPackageName());
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9b0cd25..e366e72 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2229,14 +2229,10 @@
     @Override
     void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        // If the view is not the topmost one in the view hierarchy and it is
-        // marked as the logical root of a view hierarchy, do not go any deeper.
-        if ((!(getParent() instanceof ViewRootImpl)) && (mPrivateFlags & IS_ROOT_NAMESPACE) != 0) {
-            return;
-        }
         for (int i = 0, count = mChildrenCount; i < count; i++) {
             View child = mChildren[i];
-            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
+                    && (child.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
                 info.addChild(child);
             }
         }
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 5c3f089..01b114c 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -152,6 +152,7 @@
     public void close() {
         mIsUsed = false;
         try {
+            mSpellCheckerSessionListenerImpl.close();
             mTextServicesManager.finishSpellCheckerService(mSpellCheckerSessionListenerImpl);
         } catch (RemoteException e) {
             // do nothing
@@ -190,6 +191,7 @@
     private static class SpellCheckerSessionListenerImpl extends ISpellCheckerSessionListener.Stub {
         private static final int TASK_CANCEL = 1;
         private static final int TASK_GET_SUGGESTIONS_MULTIPLE = 2;
+        private static final int TASK_CLOSE = 3;
         private final Queue<SpellCheckerParams> mPendingTasks =
                 new LinkedList<SpellCheckerParams>();
         private final Handler mHandler;
@@ -224,6 +226,9 @@
                 case TASK_GET_SUGGESTIONS_MULTIPLE:
                     processGetSuggestionsMultiple(scp);
                     break;
+                case TASK_CLOSE:
+                    processClose();
+                    break;
             }
         }
 
@@ -247,6 +252,13 @@
                             suggestionsLimit, sequentialWords));
         }
 
+        public void close() {
+            if (DBG) {
+                Log.w(TAG, "close");
+            }
+            processOrEnqueueTask(new SpellCheckerParams(TASK_CLOSE, null, 0, false));
+        }
+
         public boolean isDisconnected() {
             return mOpened && mISpellCheckerSession == null;
         }
@@ -284,6 +296,21 @@
             }
         }
 
+        private void processClose() {
+            if (!checkOpenConnection()) {
+                return;
+            }
+            if (DBG) {
+                Log.w(TAG, "Close spell checker tasks.");
+            }
+            try {
+                mISpellCheckerSession.onClose();
+                mISpellCheckerSession = null;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to close " + e);
+            }
+        }
+
         private void processGetSuggestionsMultiple(SpellCheckerParams scp) {
             if (!checkOpenConnection()) {
                 return;
diff --git a/core/java/android/webkit/HTML5VideoInline.java b/core/java/android/webkit/HTML5VideoInline.java
index 42581c2..fe5908e 100644
--- a/core/java/android/webkit/HTML5VideoInline.java
+++ b/core/java/android/webkit/HTML5VideoInline.java
@@ -74,11 +74,13 @@
     public SurfaceTexture getSurfaceTexture(int videoLayerId) {
         // Create the surface texture.
         if (videoLayerId != mVideoLayerUsingSurfaceTexture
-            || mSurfaceTexture == null) {
-            if (mTextureNames == null) {
-                mTextureNames = new int[1];
-                GLES20.glGenTextures(1, mTextureNames, 0);
+            || mSurfaceTexture == null
+            || mTextureNames == null) {
+            if (mTextureNames != null) {
+                GLES20.glDeleteTextures(1, mTextureNames, 0);
             }
+            mTextureNames = new int[1];
+            GLES20.glGenTextures(1, mTextureNames, 0);
             mSurfaceTexture = new SurfaceTexture(mTextureNames[0]);
         }
         mVideoLayerUsingSurfaceTexture = videoLayerId;
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 22e86c7..754d6e9 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2416,7 +2416,11 @@
         if (mIsRestored) {
             mInitialViewState.mIsRestored = true;
             mInitialViewState.mViewScale = mRestoredScale;
-            mInitialViewState.mTextWrapScale = mRestoredTextWrapScale;
+            if (mRestoredTextWrapScale > 0) {
+                mInitialViewState.mTextWrapScale = mRestoredTextWrapScale;
+            } else {
+                mInitialViewState.mTextWrapScale = mInitialViewState.mViewScale;
+            }
         } else {
             if (mViewportInitialScale > 0) {
                 mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale =
@@ -2535,9 +2539,11 @@
     // called by JNI
     private void restoreScale(float scale, float textWrapScale) {
         if (mBrowserFrame.firstLayoutDone() == false) {
-            mIsRestored = true;
+            mIsRestored = scale > 0;
             mRestoredScale = scale;
-            mRestoredTextWrapScale = textWrapScale;
+            if (mSettings.getUseWideViewPort()) {
+                mRestoredTextWrapScale = textWrapScale;
+            }
         }
     }
 
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index e829571..f599dba 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -1114,7 +1114,7 @@
             float scale;
             if (mInitialScale > 0) {
                 scale = mInitialScale;
-            } else if (viewState.mIsRestored) {
+            } else if (viewState.mIsRestored || viewState.mViewScale > 0) {
                 scale = (viewState.mViewScale > 0)
                     ? viewState.mViewScale : overviewScale;
                 mTextWrapScale = (viewState.mTextWrapScale > 0)
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index 9cbe8db4..e0403ff 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -16,8 +16,6 @@
 
 package android.widget;
 
-import com.android.internal.R;
-
 import android.annotation.Widget;
 import android.app.Service;
 import android.content.Context;
@@ -31,7 +29,6 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
-import android.text.format.DateFormat;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -44,6 +41,8 @@
 import android.view.ViewGroup;
 import android.widget.AbsListView.OnScrollListener;
 
+import com.android.internal.R;
+
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
@@ -121,11 +120,6 @@
     private static final int SCROLL_CHANGE_DELAY = 40;
 
     /**
-     * String for formatting the month name in the title text view.
-     */
-    private static final String FORMAT_MONTH_NAME = "MMMM, yyyy";
-
-    /**
      * String for parsing dates.
      */
     private static final String DATE_FORMAT = "MM/dd/yyyy";
@@ -940,11 +934,17 @@
      * @param calendar A day in the new focus month.
      */
     private void setMonthDisplayed(Calendar calendar) {
-        mMonthName.setText(DateFormat.format(FORMAT_MONTH_NAME, calendar));
-        mMonthName.invalidate();
-        mCurrentMonthDisplayed = calendar.get(Calendar.MONTH);
-        mAdapter.setFocusMonth(mCurrentMonthDisplayed);
-        // TODO Send Accessibility Event
+        final int newMonthDisplayed = calendar.get(Calendar.MONTH);
+        if (mCurrentMonthDisplayed != newMonthDisplayed) {
+            mCurrentMonthDisplayed = newMonthDisplayed;
+            mAdapter.setFocusMonth(mCurrentMonthDisplayed);
+            final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY
+                    | DateUtils.FORMAT_SHOW_YEAR;
+            final long millis = calendar.getTimeInMillis();
+            String newMonthName = DateUtils.formatDateRange(mContext, millis, millis, flags);
+            mMonthName.setText(newMonthName);
+            mMonthName.invalidate();
+        }
     }
 
     /**
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 320c650..5ab99dc 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -203,6 +203,11 @@
     private final EditText mInputText;
 
     /**
+     * The min height of this widget.
+     */
+    private final int mMinHeight;
+
+    /**
      * The max height of this widget.
      */
     private final int mMaxHeight;
@@ -210,7 +215,17 @@
     /**
      * The max width of this widget.
      */
-    private final int mMaxWidth;
+    private final int mMinWidth;
+
+    /**
+     * The max width of this widget.
+     */
+    private int mMaxWidth;
+
+    /**
+     * Flag whether to compute the max width.
+     */
+    private final boolean mComputeMaxWidth;
 
     /**
      * The height of the text.
@@ -527,8 +542,19 @@
                 getResources().getDisplayMetrics());
         mSelectionDividerHeight = attributesArray.getDimensionPixelSize(
                 R.styleable.NumberPicker_selectionDividerHeight, defSelectionDividerHeight);
-        mMaxHeight = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_maxHeight, 0);
-        mMaxWidth = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_maxWidth, 0);
+        mMinHeight = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_minHeight, 0);
+        mMaxHeight = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_maxHeight,
+                Integer.MAX_VALUE);
+        if (mMinHeight > mMaxHeight) {
+            throw new IllegalArgumentException("minHeight > maxHeight");
+        }
+        mMinWidth = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_minWidth, 0);
+        mMaxWidth = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_maxWidth,
+                Integer.MAX_VALUE);
+        if (mMinWidth > mMaxWidth) {
+            throw new IllegalArgumentException("minWidth > maxWidth");
+        }
+        mComputeMaxWidth = (mMaxWidth == Integer.MAX_VALUE);
         attributesArray.recycle();
 
         mShowInputControlsAnimimationDuration = getResources().getInteger(
@@ -677,37 +703,33 @@
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (mMaxHeight <= 0 && mMaxWidth <= 0) {
-            super.onLayout(changed, left, top, right, bottom);
-        } else {
-            final int msrdWdth = getMeasuredWidth();
-            final int msrdHght = getMeasuredHeight();
+        final int msrdWdth = getMeasuredWidth();
+        final int msrdHght = getMeasuredHeight();
 
-            // Increment button at the top.
-            final int inctBtnMsrdWdth = mIncrementButton.getMeasuredWidth();
-            final int incrBtnLeft = (msrdWdth - inctBtnMsrdWdth) / 2;
-            final int incrBtnTop = 0;
-            final int incrBtnRight = incrBtnLeft + inctBtnMsrdWdth;
-            final int incrBtnBottom = incrBtnTop + mIncrementButton.getMeasuredHeight();
-            mIncrementButton.layout(incrBtnLeft, incrBtnTop, incrBtnRight, incrBtnBottom);
+        // Increment button at the top.
+        final int inctBtnMsrdWdth = mIncrementButton.getMeasuredWidth();
+        final int incrBtnLeft = (msrdWdth - inctBtnMsrdWdth) / 2;
+        final int incrBtnTop = 0;
+        final int incrBtnRight = incrBtnLeft + inctBtnMsrdWdth;
+        final int incrBtnBottom = incrBtnTop + mIncrementButton.getMeasuredHeight();
+        mIncrementButton.layout(incrBtnLeft, incrBtnTop, incrBtnRight, incrBtnBottom);
 
-            // Input text centered horizontally.
-            final int inptTxtMsrdWdth = mInputText.getMeasuredWidth();
-            final int inptTxtMsrdHght = mInputText.getMeasuredHeight();
-            final int inptTxtLeft = (msrdWdth - inptTxtMsrdWdth) / 2;
-            final int inptTxtTop = (msrdHght - inptTxtMsrdHght) / 2;
-            final int inptTxtRight = inptTxtLeft + inptTxtMsrdWdth;
-            final int inptTxtBottom = inptTxtTop + inptTxtMsrdHght;
-            mInputText.layout(inptTxtLeft, inptTxtTop, inptTxtRight, inptTxtBottom);
+        // Input text centered horizontally.
+        final int inptTxtMsrdWdth = mInputText.getMeasuredWidth();
+        final int inptTxtMsrdHght = mInputText.getMeasuredHeight();
+        final int inptTxtLeft = (msrdWdth - inptTxtMsrdWdth) / 2;
+        final int inptTxtTop = (msrdHght - inptTxtMsrdHght) / 2;
+        final int inptTxtRight = inptTxtLeft + inptTxtMsrdWdth;
+        final int inptTxtBottom = inptTxtTop + inptTxtMsrdHght;
+        mInputText.layout(inptTxtLeft, inptTxtTop, inptTxtRight, inptTxtBottom);
 
-            // Decrement button at the top.
-            final int decrBtnMsrdWdth = mIncrementButton.getMeasuredWidth();
-            final int decrBtnLeft = (msrdWdth - decrBtnMsrdWdth) / 2;
-            final int decrBtnTop = msrdHght - mDecrementButton.getMeasuredHeight();
-            final int decrBtnRight = decrBtnLeft + decrBtnMsrdWdth;
-            final int decrBtnBottom = msrdHght;
-            mDecrementButton.layout(decrBtnLeft, decrBtnTop, decrBtnRight, decrBtnBottom);
-        }
+        // Decrement button at the top.
+        final int decrBtnMsrdWdth = mIncrementButton.getMeasuredWidth();
+        final int decrBtnLeft = (msrdWdth - decrBtnMsrdWdth) / 2;
+        final int decrBtnTop = msrdHght - mDecrementButton.getMeasuredHeight();
+        final int decrBtnRight = decrBtnLeft + decrBtnMsrdWdth;
+        final int decrBtnBottom = msrdHght;
+        mDecrementButton.layout(decrBtnLeft, decrBtnTop, decrBtnRight, decrBtnBottom);
 
         if (!mScrollWheelAndFadingEdgesInitialized) {
             mScrollWheelAndFadingEdgesInitialized = true;
@@ -719,20 +741,9 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        final int measuredWidth;
-        if (mMaxWidth > 0) {
-            measuredWidth = getMaxSize(widthMeasureSpec, mMaxWidth);
-        } else {
-            measuredWidth = getMeasuredWidth();
-        }
-        final int measuredHeight;
-        if (mMaxHeight > 0) {
-            measuredHeight = getMaxSize(heightMeasureSpec, mMaxHeight);
-        } else {
-            measuredHeight = getMeasuredHeight();
-        }
-        setMeasuredDimension(measuredWidth, measuredHeight);
+        final int newWidthMeasureSpec = makeMeasureSpec(widthMeasureSpec, mMinWidth, mMaxWidth);
+        final int newHeightMeasureSpec = makeMeasureSpec(heightMeasureSpec, mMinHeight, mMaxHeight);
+        super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
     }
 
     @Override
@@ -1034,6 +1045,49 @@
     }
 
     /**
+     * Computes the max width if no such specified as an attribute.
+     */
+    private void tryComputeMaxWidth() {
+        if (!mComputeMaxWidth) {
+            return;
+        }
+        int maxTextWidth = 0;
+        if (mDisplayedValues == null) {
+            float maxDigitWidth = 0;
+            for (int i = 0; i <= 9; i++) {
+                final float digitWidth = mSelectorWheelPaint.measureText(String.valueOf(i));
+                if (digitWidth > maxDigitWidth) {
+                    maxDigitWidth = digitWidth;
+                }
+            }
+            int numberOfDigits = 0;
+            int current = mMaxValue;
+            while (current > 0) {
+                numberOfDigits++;
+                current = current / 10;
+            }
+            maxTextWidth = (int) (numberOfDigits * maxDigitWidth);
+        } else {
+            final int valueCount = mDisplayedValues.length;
+            for (int i = 0; i < valueCount; i++) {
+                final float textWidth = mSelectorWheelPaint.measureText(mDisplayedValues[i]);
+                if (textWidth > maxTextWidth) {
+                    maxTextWidth = (int) textWidth;
+                }
+            }
+        }
+        maxTextWidth += mInputText.getPaddingLeft() + mInputText.getPaddingRight();
+        if (mMaxWidth != maxTextWidth) {
+            if (maxTextWidth > mMinWidth) {
+                mMaxWidth = maxTextWidth;
+            } else {
+                mMaxWidth = mMinWidth;
+            }
+            invalidate();
+        }
+    }
+
+    /**
      * Gets whether the selector wheel wraps when reaching the min/max value.
      *
      * @return True if the selector wheel wraps.
@@ -1119,6 +1173,7 @@
         setWrapSelectorWheel(wrapSelectorWheel);
         initializeSelectorWheelIndices();
         updateInputTextView();
+        tryComputeMaxWidth();
     }
 
     /**
@@ -1150,6 +1205,7 @@
         setWrapSelectorWheel(wrapSelectorWheel);
         initializeSelectorWheelIndices();
         updateInputTextView();
+        tryComputeMaxWidth();
     }
 
     /**
@@ -1298,24 +1354,28 @@
     }
 
     /**
-     * Gets the max value for a size based on the measure spec passed by
-     * the parent and the max value for that size.
+     * Makes a measure spec that tries greedily to use the max value.
      *
      * @param measureSpec The measure spec.
      * @param maxValue The max value for the size.
-     * @return The max size.
+     * @return A measure spec greedily imposing the max size.
      */
-    private int getMaxSize(int measureSpec, int maxValue) {
+    private int makeMeasureSpec(int measureSpec, int minValue, int maxValue) {
+        final int size = MeasureSpec.getSize(measureSpec);
+        if (size < minValue) {
+            throw new IllegalArgumentException("Available space is less than min size: "
+                    +  size + " < " + minValue);
+        }
         final int mode = MeasureSpec.getMode(measureSpec);
         switch (mode) {
             case MeasureSpec.EXACTLY:
-                return MeasureSpec.getSize(measureSpec);
+                return measureSpec;
             case MeasureSpec.AT_MOST:
-                return Math.min(MeasureSpec.getSize(measureSpec), maxValue);
+                return MeasureSpec.makeMeasureSpec(Math.min(size, maxValue), MeasureSpec.EXACTLY);
             case MeasureSpec.UNSPECIFIED:
-                return maxValue;
+                return MeasureSpec.makeMeasureSpec(maxValue, MeasureSpec.EXACTLY);
             default:
-                throw new IllegalArgumentException();
+                throw new IllegalArgumentException("Unknown measure mode: " + mode);
         }
     }
 
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 6df80e4..399d217 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -1187,6 +1187,8 @@
      */
     @Override
     public void onActionViewExpanded() {
+        if (mExpandedInActionView) return;
+
         mExpandedInActionView = true;
         mCollapsedImeOptions = mQueryTextView.getImeOptions();
         mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index ee3f23b..41993c4 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -25,12 +25,14 @@
 import android.os.SystemClock;
 import android.util.Slog;
 
+import com.android.internal.util.ProcFileReader;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 import com.google.android.collect.Sets;
 
 import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -107,6 +109,7 @@
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
         final NetworkStats.Entry entry = new NetworkStats.Entry();
 
+        // TODO: transition to ProcFileReader
         // TODO: read directly from proc once headers are added
         final ArrayList<String> keys = Lists.newArrayList(KEY_IFACE, KEY_ACTIVE, KEY_SNAP_RX_BYTES,
                 KEY_SNAP_RX_PACKETS, KEY_SNAP_TX_BYTES, KEY_SNAP_TX_PACKETS, KEY_RX_BYTES,
@@ -257,71 +260,58 @@
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
         final NetworkStats.Entry entry = new NetworkStats.Entry();
 
-        // TODO: remove knownLines check once 5087722 verified
-        final HashSet<String> knownLines = Sets.newHashSet();
-        // TODO: remove lastIdx check once 5270106 verified
-        int lastIdx;
+        int idx = 1;
+        int lastIdx = 1;
 
-        final ArrayList<String> keys = Lists.newArrayList();
-        final ArrayList<String> values = Lists.newArrayList();
-        final HashMap<String, String> parsed = Maps.newHashMap();
-
-        BufferedReader reader = null;
-        String line = null;
+        ProcFileReader reader = null;
         try {
-            reader = new BufferedReader(new FileReader(mStatsXtUid));
+            // open and consume header line
+            reader = new ProcFileReader(new FileInputStream(mStatsXtUid));
+            reader.finishLine();
 
-            // parse first line as header
-            line = reader.readLine();
-            splitLine(line, keys);
-            lastIdx = 1;
-
-            // parse remaining lines
-            while ((line = reader.readLine()) != null) {
-                splitLine(line, values);
-                parseLine(keys, values, parsed);
-
-                if (!knownLines.add(line)) {
-                    throw new IllegalStateException("duplicate proc entry: " + line);
-                }
-
-                final int idx = getParsedInt(parsed, KEY_IDX);
+            while (reader.hasMoreData()) {
+                idx = reader.nextInt();
                 if (idx != lastIdx + 1) {
                     throw new IllegalStateException(
                             "inconsistent idx=" + idx + " after lastIdx=" + lastIdx);
                 }
                 lastIdx = idx;
 
-                entry.iface = parsed.get(KEY_IFACE);
-                entry.uid = getParsedInt(parsed, KEY_UID);
-                entry.set = getParsedInt(parsed, KEY_COUNTER_SET);
-                entry.tag = kernelToTag(parsed.get(KEY_TAG_HEX));
-                entry.rxBytes = getParsedLong(parsed, KEY_RX_BYTES);
-                entry.rxPackets = getParsedLong(parsed, KEY_RX_PACKETS);
-                entry.txBytes = getParsedLong(parsed, KEY_TX_BYTES);
-                entry.txPackets = getParsedLong(parsed, KEY_TX_PACKETS);
+                entry.iface = reader.nextString();
+                entry.tag = kernelToTag(reader.nextString());
+                entry.uid = reader.nextInt();
+                entry.set = reader.nextInt();
+                entry.rxBytes = reader.nextLong();
+                entry.rxPackets = reader.nextLong();
+                entry.txBytes = reader.nextLong();
+                entry.txPackets = reader.nextLong();
 
                 if (limitUid == UID_ALL || limitUid == entry.uid) {
                     stats.addValues(entry);
                 }
+
+                reader.finishLine();
             }
         } catch (NullPointerException e) {
-            throw new IllegalStateException("problem parsing line: " + line, e);
+            throw new IllegalStateException("problem parsing idx " + idx, e);
         } catch (NumberFormatException e) {
-            throw new IllegalStateException("problem parsing line: " + line, e);
+            throw new IllegalStateException("problem parsing idx " + idx, e);
         } catch (IOException e) {
-            throw new IllegalStateException("problem parsing line: " + line, e);
+            throw new IllegalStateException("problem parsing idx " + idx, e);
         } finally {
             IoUtils.closeQuietly(reader);
         }
+
         return stats;
     }
 
+    @Deprecated
     private static int getParsedInt(HashMap<String, String> parsed, String key) {
         final String value = parsed.get(key);
         return value != null ? Integer.parseInt(value) : 0;
     }
 
+    @Deprecated
     private static long getParsedLong(HashMap<String, String> parsed, String key) {
         final String value = parsed.get(key);
         return value != null ? Long.parseLong(value) : 0;
@@ -330,6 +320,7 @@
     /**
      * Split given line into {@link ArrayList}.
      */
+    @Deprecated
     private static void splitLine(String line, ArrayList<String> outSplit) {
         outSplit.clear();
 
@@ -343,6 +334,7 @@
      * Zip the two given {@link ArrayList} as key and value pairs into
      * {@link HashMap}.
      */
+    @Deprecated
     private static void parseLine(
             ArrayList<String> keys, ArrayList<String> values, HashMap<String, String> outParsed) {
         outParsed.clear();
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
index 5a00603..3c61968 100644
--- a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
+++ b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
@@ -25,4 +25,5 @@
     void onGetSuggestionsMultiple(
             in TextInfo[] textInfos, int suggestionsLimit, boolean multipleWords);
     void onCancel();
+    void onClose();
 }
diff --git a/core/java/com/android/internal/util/ProcFileReader.java b/core/java/com/android/internal/util/ProcFileReader.java
new file mode 100644
index 0000000..72e1f0f
--- /dev/null
+++ b/core/java/com/android/internal/util/ProcFileReader.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charsets;
+
+/**
+ * Reader that specializes in parsing {@code /proc/} files quickly. Walks
+ * through the stream using a single space {@code ' '} as token separator, and
+ * requires each line boundary to be explicitly acknowledged using
+ * {@link #finishLine()}. Assumes {@link Charsets#US_ASCII} encoding.
+ * <p>
+ * Currently doesn't support formats based on {@code \0}, tabs, or repeated
+ * delimiters.
+ */
+public class ProcFileReader implements Closeable {
+    private final InputStream mStream;
+    private final byte[] mBuffer;
+
+    /** Write pointer in {@link #mBuffer}. */
+    private int mTail;
+    /** Flag when last read token finished current line. */
+    private boolean mLineFinished;
+
+    public ProcFileReader(InputStream stream) throws IOException {
+        this(stream, 4096);
+    }
+
+    public ProcFileReader(InputStream stream, int bufferSize) throws IOException {
+        mStream = stream;
+        mBuffer = new byte[bufferSize];
+
+        // read enough to answer hasMoreData
+        fillBuf();
+    }
+
+    /**
+     * Read more data from {@link #mStream} into internal buffer.
+     */
+    private int fillBuf() throws IOException {
+        final int length = mBuffer.length - mTail;
+        if (length == 0) {
+            throw new IOException("attempting to fill already-full buffer");
+        }
+
+        final int read = mStream.read(mBuffer, mTail, length);
+        if (read != -1) {
+            mTail += read;
+        }
+        return read;
+    }
+
+    /**
+     * Consume number of bytes from beginning of internal buffer. If consuming
+     * all remaining bytes, will attempt to {@link #fillBuf()}.
+     */
+    private void consumeBuf(int count) throws IOException {
+        // TODO: consider moving to read pointer, but for now traceview says
+        // these copies aren't a bottleneck.
+        System.arraycopy(mBuffer, count, mBuffer, 0, mTail - count);
+        mTail -= count;
+        if (mTail == 0) {
+            fillBuf();
+        }
+    }
+
+    /**
+     * Find buffer index of next token delimiter, usually space or newline. Will
+     * fill buffer as needed.
+     */
+    private int nextTokenIndex() throws IOException {
+        if (mLineFinished) {
+            throw new IOException("no tokens remaining on current line");
+        }
+
+        int i = 0;
+        do {
+            // scan forward for token boundary
+            for (; i < mTail; i++) {
+                final byte b = mBuffer[i];
+                if (b == '\n') {
+                    mLineFinished = true;
+                    return i;
+                }
+                if (b == ' ') {
+                    return i;
+                }
+            }
+        } while (fillBuf() > 0);
+
+        throw new IOException("end of stream while looking for token boundary");
+    }
+
+    /**
+     * Check if stream has more data to be parsed.
+     */
+    public boolean hasMoreData() {
+        return mTail > 0;
+    }
+
+    /**
+     * Finish current line, skipping any remaining data.
+     */
+    public void finishLine() throws IOException {
+        // last token already finished line; reset silently
+        if (mLineFinished) {
+            mLineFinished = false;
+            return;
+        }
+
+        int i = 0;
+        do {
+            // scan forward for line boundary and consume
+            for (; i < mTail; i++) {
+                if (mBuffer[i] == '\n') {
+                    consumeBuf(i + 1);
+                    return;
+                }
+            }
+        } while (fillBuf() > 0);
+
+        throw new IOException("end of stream while looking for line boundary");
+    }
+
+    /**
+     * Parse and return next token as {@link String}.
+     */
+    public String nextString() throws IOException {
+        final int tokenIndex = nextTokenIndex();
+        final String s = new String(mBuffer, 0, tokenIndex, Charsets.US_ASCII);
+        consumeBuf(tokenIndex + 1);
+        return s;
+    }
+
+    /**
+     * Parse and return next token as base-10 encoded {@code long}.
+     */
+    public long nextLong() throws IOException {
+        final int tokenIndex = nextTokenIndex();
+        final boolean negative = mBuffer[0] == '-';
+
+        // TODO: refactor into something like IntegralToString
+        long result = 0;
+        for (int i = negative ? 1 : 0; i < tokenIndex; i++) {
+            final int digit = mBuffer[i] - '0';
+            if (digit < 0 || digit > 9) {
+                throw invalidLong(tokenIndex);
+            }
+
+            // always parse as negative number and apply sign later; this
+            // correctly handles MIN_VALUE which is "larger" than MAX_VALUE.
+            final long next = result * 10 - digit;
+            if (next > result) {
+                throw invalidLong(tokenIndex);
+            }
+            result = next;
+        }
+
+        consumeBuf(tokenIndex + 1);
+        return negative ? result : -result;
+    }
+
+    private NumberFormatException invalidLong(int tokenIndex) {
+        return new NumberFormatException(
+                "invalid long: " + new String(mBuffer, 0, tokenIndex, Charsets.US_ASCII));
+    }
+
+    /**
+     * Parse and return next token as base-10 encoded {@code int}.
+     */
+    public int nextInt() throws IOException {
+        final long value = nextLong();
+        if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
+            throw new NumberFormatException("parsed value larger than integer");
+        }
+        return (int) value;
+    }
+
+    public void close() throws IOException {
+        mStream.close();
+    }
+}
diff --git a/core/res/res/drawable/ic_lockscreen_outerring.xml b/core/res/res/drawable/ic_lockscreen_outerring.xml
index 490f370..3bdd6f6 100644
--- a/core/res/res/drawable/ic_lockscreen_outerring.xml
+++ b/core/res/res/drawable/ic_lockscreen_outerring.xml
@@ -17,7 +17,7 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="oval"
     >
-    <size android:height="270dp" android:width="270dp" />
+    <size android:height="@dimen/keyguard_lockscreen_outerring_diameter" android:width="@dimen/keyguard_lockscreen_outerring_diameter" />
     <solid android:color="#00000000" />
     <stroke android:color="#1affffff" android:width="2dp" />
-</shape>
+</shape>
\ No newline at end of file
diff --git a/core/res/res/layout-sw600dp/date_picker_dialog.xml b/core/res/res/layout-sw600dp/date_picker_dialog.xml
index 004d52a..f9b247f 100644
--- a/core/res/res/layout-sw600dp/date_picker_dialog.xml
+++ b/core/res/res/layout-sw600dp/date_picker_dialog.xml
@@ -22,4 +22,6 @@
     android:layout_gravity="center_horizontal"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
+    android:spinnersShown="true"
+    android:calendarViewShown="true"
     />
diff --git a/core/res/res/layout-sw600dp/number_picker.xml b/core/res/res/layout-sw600dp/number_picker.xml
deleted file mode 100644
index 807daf2..0000000
--- a/core/res/res/layout-sw600dp/number_picker.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <ImageButton android:id="@+id/increment"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        style="?android:attr/numberPickerUpButtonStyle"
-        android:contentDescription="@string/number_picker_increment_button" />
-
-    <EditText android:id="@+id/numberpicker_input"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        style="?android:attr/numberPickerInputTextStyle" />
-
-    <ImageButton android:id="@+id/decrement"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        style="?android:attr/numberPickerDownButtonStyle"
-        android:contentDescription="@string/number_picker_decrement_button" />
-
-</merge>
diff --git a/core/res/res/layout/date_picker.xml b/core/res/res/layout/date_picker.xml
index 1649466..6f0517d 100644
--- a/core/res/res/layout/date_picker.xml
+++ b/core/res/res/layout/date_picker.xml
@@ -40,7 +40,7 @@
         <!-- Month -->
         <NumberPicker
             android:id="@+id/month"
-            android:layout_width="80dip"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginLeft="1dip"
             android:layout_marginRight="1dip"
@@ -51,7 +51,7 @@
         <!-- Day -->
         <NumberPicker
             android:id="@+id/day"
-            android:layout_width="80dip"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginLeft="1dip"
             android:layout_marginRight="1dip"
@@ -62,7 +62,7 @@
         <!-- Year -->
         <NumberPicker
             android:id="@+id/year"
-            android:layout_width="95dip"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginLeft="1dip"
             android:layout_marginRight="1dip"
diff --git a/core/res/res/layout/date_picker_holo.xml b/core/res/res/layout/date_picker_holo.xml
index 8627637..57b5614 100644
--- a/core/res/res/layout/date_picker_holo.xml
+++ b/core/res/res/layout/date_picker_holo.xml
@@ -23,8 +23,8 @@
      depending on the date format selected by the user.
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
     android:layout_gravity="center_horizontal"
     android:orientation="horizontal"
     android:gravity="center">
@@ -32,7 +32,6 @@
     <LinearLayout android:id="@+id/pickers"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginRight="22dip"
         android:layout_weight="1"
         android:orientation="horizontal"
         android:gravity="center">
@@ -40,10 +39,10 @@
         <!-- Month -->
         <NumberPicker
             android:id="@+id/month"
-            android:layout_width="48dip"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginLeft="22dip"
-            android:layout_marginRight="22dip"
+            android:layout_marginLeft="16dip"
+            android:layout_marginRight="16dip"
             android:focusable="true"
             android:focusableInTouchMode="true"
             />
@@ -51,10 +50,10 @@
         <!-- Day -->
         <NumberPicker
             android:id="@+id/day"
-            android:layout_width="48dip"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginLeft="22dip"
-            android:layout_marginRight="22dip"
+            android:layout_marginLeft="16dip"
+            android:layout_marginRight="16dip"
             android:focusable="true"
             android:focusableInTouchMode="true"
             />
@@ -62,10 +61,10 @@
         <!-- Year -->
         <NumberPicker
             android:id="@+id/year"
-            android:layout_width="48dip"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginLeft="22dip"
-            android:layout_marginRight="22dip"
+            android:layout_marginLeft="16dip"
+            android:layout_marginRight="16dip"
             android:focusable="true"
             android:focusableInTouchMode="true"
             />
diff --git a/core/res/res/layout/time_picker_holo.xml b/core/res/res/layout/time_picker_holo.xml
index ca6fe2d..29c97b7 100644
--- a/core/res/res/layout/time_picker_holo.xml
+++ b/core/res/res/layout/time_picker_holo.xml
@@ -28,10 +28,10 @@
     <!-- hour -->
     <NumberPicker
         android:id="@+id/hour"
-        android:layout_width="48dip"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginLeft="22dip"
-        android:layout_marginRight="20dip"
+        android:layout_marginLeft="16dip"
+        android:layout_marginRight="14dip"
         android:focusable="true"
         android:focusableInTouchMode="true"
         />
@@ -47,10 +47,10 @@
     <!-- minute -->
     <NumberPicker
         android:id="@+id/minute"
-        android:layout_width="48dip"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginLeft="20dip"
-        android:layout_marginRight="22dip"
+        android:layout_marginLeft="14dip"
+        android:layout_marginRight="16dip"
         android:focusable="true"
         android:focusableInTouchMode="true"
         />
@@ -58,10 +58,10 @@
     <!-- AM / PM -->
     <NumberPicker
         android:id="@+id/amPm"
-        android:layout_width="48dip"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginLeft="22dip"
-        android:layout_marginRight="22dip"
+        android:layout_marginLeft="16dip"
+        android:layout_marginRight="16dip"
         android:focusable="true"
         android:focusableInTouchMode="true"
         />
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index db94884..551c1d8 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -45,6 +45,12 @@
     <!-- Size of clock font in LockScreen. -->
     <dimen name="keyguard_pattern_unlock_clock_font_size">98sp</dimen>
 
+    <!-- Size of lockscreen outerring on unsecure unlock LockScreen -->
+    <dimen name="keyguard_lockscreen_outerring_diameter">364dp</dimen>
+
+    <!-- target placement radius for MultiWaveView -->
+    <dimen name="multiwaveview_target_placement_radius">182dip</dimen>
+
     <!-- Size of status line font in LockScreen. -->
     <dimen name="keyguard_pattern_unlock_status_line_font_size">14sp</dimen>
 
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index ce6bbc2..f9e1f5b 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -343,8 +343,6 @@
        <item>@drawable/list_divider_holo_dark</item>
        <item>@drawable/list_divider_holo_light</item>
        <item>@drawable/list_divider_holo_light</item>
-       <item>@drawable/ic_lockscreen_handle</item>
-       <item>@drawable/ic_lockscreen_outerring</item>
        <item>@drawable/ic_lockscreen_chevron_left</item>
        <item>@drawable/ic_lockscreen_chevron_right</item>
        <item>@drawable/ab_transparent_dark_holo</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 936482df..a40e24c 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3576,8 +3576,14 @@
         <attr name="selectionDivider" format="reference" />
         <!-- @hide The height of the selection divider. -->
         <attr name="selectionDividerHeight" format="dimension" />
+        <!-- @hide The min height of the NumberPicker. -->
+        <attr name="minHeight" />
         <!-- @hide The max height of the NumberPicker. -->
         <attr name="maxHeight" />
+        <!-- @hide The min width of the NumberPicker. -->
+        <attr name="minWidth" />
+        <!-- @hide The max width of the NumberPicker. -->
+        <attr name="maxWidth" />
         <!-- @hide The max width of the NumberPicker. -->
         <attr name="maxWidth" />
     </declare-styleable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index bde2c72..48e8f1e 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -182,12 +182,14 @@
     <!-- Regex of wired ethernet ifaces -->
     <string translatable="false" name="config_ethernet_iface_regex">eth\\d</string>
 
-    <!-- If the mobile hotspot feature requires provisioning, an intent string can be provided
-        to the launch a supported application that provisions the devices.
+    <!-- If the mobile hotspot feature requires provisioning, a package name and class name
+        can be provided to launch a supported application that provisions the devices.
 
         Example Usage:
 
-        Intent intent = new Intent(R.string.config_mobile_hotspot_provision_intent);
+        String[] appDetails = getStringArray(R.array.config_mobile_hotspot_provision_app);
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.setClassName(appDetails[0], appDetails[1]);
         startActivityForResult(intent, 0);
 
         public void onActivityResult(int requestCode, int resultCode, Intent intent) {
@@ -202,7 +204,14 @@
 
         See src/com/android/settings/TetherSettings.java for more details.
         -->
-    <string translatable="false" name="config_mobile_hotspot_provision_intent"></string>
+    <!-- The first element is the package name and the second element is the class name
+         of the provisioning app -->
+    <string-array translatable="false" name="config_mobile_hotspot_provision_app">
+    <!--
+        <item>com.example.provisioning</item>
+        <item>com.example.provisioning.Activity</item>
+    -->
+    </string-array>
 
     <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
     <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 3edd5ce..fa18648 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -66,6 +66,9 @@
     <!-- Default vertical gap between keys in the password keyboard (used by keyguard) -->
     <dimen name="password_keyboard_verticalGap">9dip</dimen>
 
+    <!-- Size of lockscreen outerring on unsecure unlock LockScreen -->
+    <dimen name="keyguard_lockscreen_outerring_diameter">270dp</dimen>
+
     <!-- Default target placement radius for MultiWaveView -->
     <dimen name="multiwaveview_target_placement_radius">135dip</dimen>
 
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 8356985..8d95d86e5 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1654,8 +1654,8 @@
         <item name="android:flingable">true</item>
         <item name="android:selectionDivider">@android:drawable/numberpicker_selection_divider</item>
         <item name="android:selectionDividerHeight">2dip</item>
-        <item name="android:maxHeight">180dip</item>
-        <item name="android:maxWidth">56dip</item>
+        <item name="android:minWidth">48dip</item>
+        <item name="android:maxHeight">200dip</item>
     </style>
 
     <style name="Widget.Holo.TimePicker" parent="Widget.TimePicker">
@@ -1684,6 +1684,8 @@
     <style name="Widget.Holo.EditText.NumberPickerInputText">
         <item name="android:paddingTop">13sp</item>
         <item name="android:paddingBottom">13sp</item>
+        <item name="android:paddingLeft">2sp</item>
+        <item name="android:paddingRight">2sp</item>
         <item name="android:gravity">center</item>
         <item name="android:singleLine">true</item>
         <item name="android:textSize">18sp</item>
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
index 9eee2f0..0cc883f 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
@@ -89,7 +89,7 @@
      * Ensure that downloading on wifi reports reasonable stats.
      */
     @LargeTest
-    public void testWifiDownload() {
+    public void testWifiDownload() throws Exception {
         assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
         NetworkStats pre_test_stats = fetchDataFromProc(mUid);
         String ts = Long.toString(System.currentTimeMillis());
@@ -123,7 +123,7 @@
      * Ensure that downloading on wifi reports reasonable stats.
      */
     @LargeTest
-    public void testWifiUpload() {
+    public void testWifiUpload() throws Exception {
         assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
         // Download a file from the server.
         String ts = Long.toString(System.currentTimeMillis());
@@ -160,7 +160,7 @@
      * accounting still goes to the app making the call and that the numbers still make sense.
      */
     @LargeTest
-    public void testWifiDownloadWithDownloadManager() {
+    public void testWifiDownloadWithDownloadManager() throws Exception {
         assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
         // If we are using the download manager, then the data that is written to /proc/uid_stat/
         // is accounted against download manager's uid, since it uses pre-ICS API.
diff --git a/core/tests/coretests/res/raw/xt_qtaguid_extended b/core/tests/coretests/res/raw/xt_qtaguid_extended
deleted file mode 100644
index 2f3b4ec..0000000
--- a/core/tests/coretests/res/raw/xt_qtaguid_extended
+++ /dev/null
@@ -1,3 +0,0 @@
-acct_tag_hex uid_tag_int iface rx_bytes rx_packets tx_bytes tx_packets teleported_goats idx
-0x0 1000 test0 1024 10 2048 20 2716057 2
-0x0000F00D00000000 1000 test0 512 5 512 5 3370318 3
diff --git a/core/tests/coretests/res/raw/xt_qtaguid_typical b/core/tests/coretests/res/raw/xt_qtaguid_typical
index 8df4b1b..c1b0d25 100644
--- a/core/tests/coretests/res/raw/xt_qtaguid_typical
+++ b/core/tests/coretests/res/raw/xt_qtaguid_typical
@@ -1,32 +1,71 @@
-idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes
-2 wlan0 0x0 0 14615 4270
-3 wlan0 0x0 1000 5175 915
-4 wlan0 0x0 1021 3381 903
-5 wlan0 0x0 10004 333821 53558
-6 wlan0 0x0 10010 4888 37363
-7 wlan0 0x0 10013 52 104
-8 wlan0 0x74182ada00000000 10004 18725 1066
-9 rmnet0 0x0 0 301274 30244
-10 rmnet0 0x0 1000 304 441
-11 rmnet0 0x0 1013 2880 2272
-12 rmnet0 0x0 1021 31407 8430
-13 rmnet0 0x0 10003 32665 3814
-14 rmnet0 0x0 10004 2373141 420112
-15 rmnet0 0x0 10010 870370 1111727
-16 rmnet0 0x0 10013 240 240
-17 rmnet0 0x0 10016 16703 13512
-18 rmnet0 0x0 10017 3990 3269
-19 rmnet0 0x0 10018 474504 14516062
-20 rmnet0 0x0 10019 782804 71077
-21 rmnet0 0x0 10022 70671 49684
-22 rmnet0 0x0 10029 5785354 397159
-23 rmnet0 0x0 10033 2102 1686
-24 rmnet0 0x0 10034 15495464 227694
-25 rmnet0 0x0 10037 31184994 684122
-26 rmnet0 0x0 10051 298687 113485
-27 rmnet0 0x0 10056 29504 20669
-28 rmnet0 0x0 10069 683 596
-29 rmnet0 0x0 10072 34051 12453
-30 rmnet0 0x0 10077 7025393 213866
-31 rmnet0 0x0 10081 354 1178
-32 rmnet0 0x74182ada00000000 10037 28507378 437004
+idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
+2 wlan0 0x0 0 0 18621 96 2898 44 312 6 15897 58 2412 32 312 6 1010 16 1576 22
+3 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+4 wlan0 0x0 1000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+5 wlan0 0x0 1000 1 1949 13 1078 14 0 0 1600 10 349 3 0 0 600 10 478 4
+6 wlan0 0x0 10005 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+7 wlan0 0x0 10005 1 32081 38 5315 50 32081 38 0 0 0 0 5315 50 0 0 0 0
+8 wlan0 0x0 10011 0 35777 53 5718 57 0 0 0 0 35777 53 0 0 0 0 5718 57
+9 wlan0 0x0 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+10 wlan0 0x0 10014 0 0 0 1098 13 0 0 0 0 0 0 0 0 0 0 1098 13
+11 wlan0 0x0 10014 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+12 wlan0 0x0 10021 0 562386 573 49228 549 0 0 0 0 562386 573 0 0 0 0 49228 549
+13 wlan0 0x0 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+14 wlan0 0x0 10031 0 3425 5 586 6 0 0 0 0 3425 5 0 0 0 0 586 6
+15 wlan0 0x0 10031 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+16 wlan0 0x7fffff0100000000 10021 0 562386 573 49228 549 0 0 0 0 562386 573 0 0 0 0 49228 549
+17 wlan0 0x7fffff0100000000 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+18 wlan0 0x7fffff0100000000 10031 0 3425 5 586 6 0 0 0 0 3425 5 0 0 0 0 586 6
+19 wlan0 0x7fffff0100000000 10031 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+20 rmnet2 0x0 0 0 547 5 118 2 40 1 243 1 264 3 0 0 62 1 56 1
+21 rmnet2 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+22 rmnet2 0x0 10001 0 1125899906842624 5 984 11 632 5 0 0 0 0 984 11 0 0 0 0
+23 rmnet2 0x0 10001 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+24 rmnet1 0x0 0 0 26736 174 7098 130 7210 97 18382 64 1144 13 2932 64 4054 64 112 2
+25 rmnet1 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+26 rmnet1 0x0 1000 0 75774 77 18038 78 75335 72 439 5 0 0 17668 73 370 5 0 0
+27 rmnet1 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+28 rmnet1 0x0 10007 0 269945 578 111632 586 269945 578 0 0 0 0 111632 586 0 0 0 0
+29 rmnet1 0x0 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+30 rmnet1 0x0 10011 0 1741256 6918 769778 7019 1741256 6918 0 0 0 0 769778 7019 0 0 0 0
+31 rmnet1 0x0 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+32 rmnet1 0x0 10014 0 0 0 786 12 0 0 0 0 0 0 786 12 0 0 0 0
+33 rmnet1 0x0 10014 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+34 rmnet1 0x0 10021 0 433533 1454 393420 1604 433533 1454 0 0 0 0 393420 1604 0 0 0 0
+35 rmnet1 0x0 10021 1 21215 33 10278 33 21215 33 0 0 0 0 10278 33 0 0 0 0
+36 rmnet1 0x0 10036 0 6310 25 3284 29 6310 25 0 0 0 0 3284 29 0 0 0 0
+37 rmnet1 0x0 10036 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+38 rmnet1 0x0 10047 0 34264 47 3936 34 34264 47 0 0 0 0 3936 34 0 0 0 0
+39 rmnet1 0x0 10047 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+40 rmnet1 0x4e7700000000 10011 0 9187 27 4248 33 9187 27 0 0 0 0 4248 33 0 0 0 0
+41 rmnet1 0x4e7700000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+42 rmnet1 0x1000000000000000 10007 0 2109 4 791 4 2109 4 0 0 0 0 791 4 0 0 0 0
+43 rmnet1 0x1000000000000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+44 rmnet1 0x1000000400000000 10007 0 9811 22 6286 22 9811 22 0 0 0 0 6286 22 0 0 0 0
+45 rmnet1 0x1000000400000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+46 rmnet1 0x1010000000000000 10021 0 164833 426 135392 527 164833 426 0 0 0 0 135392 527 0 0 0 0
+47 rmnet1 0x1010000000000000 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+48 rmnet1 0x1144000400000000 10011 0 10112 18 3334 17 10112 18 0 0 0 0 3334 17 0 0 0 0
+49 rmnet1 0x1144000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+50 rmnet1 0x1244000400000000 10011 0 1300 3 848 2 1300 3 0 0 0 0 848 2 0 0 0 0
+51 rmnet1 0x1244000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+52 rmnet1 0x3000000000000000 10007 0 10389 14 1521 12 10389 14 0 0 0 0 1521 12 0 0 0 0
+53 rmnet1 0x3000000000000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+54 rmnet1 0x3000000400000000 10007 0 238070 380 93938 404 238070 380 0 0 0 0 93938 404 0 0 0 0
+55 rmnet1 0x3000000400000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+56 rmnet1 0x3010000000000000 10021 0 219110 578 227423 676 219110 578 0 0 0 0 227423 676 0 0 0 0
+57 rmnet1 0x3010000000000000 10021 1 742 3 1265 3 742 3 0 0 0 0 1265 3 0 0 0 0
+58 rmnet1 0x3020000000000000 10021 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+59 rmnet1 0x3020000000000000 10021 1 20473 30 9013 30 20473 30 0 0 0 0 9013 30 0 0 0 0
+60 rmnet1 0x3144000400000000 10011 0 43963 92 34414 116 43963 92 0 0 0 0 34414 116 0 0 0 0
+61 rmnet1 0x3144000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+62 rmnet1 0x3244000400000000 10011 0 3486 8 1520 9 3486 8 0 0 0 0 1520 9 0 0 0 0
+63 rmnet1 0x3244000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+64 rmnet1 0x7fffff0100000000 10021 0 29102 56 8865 60 29102 56 0 0 0 0 8865 60 0 0 0 0
+65 rmnet1 0x7fffff0100000000 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+66 rmnet1 0x7fffff0300000000 1000 0 995 13 14145 14 995 13 0 0 0 0 14145 14 0 0 0 0
+67 rmnet1 0x7fffff0300000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+68 rmnet0 0x0 0 0 4312 49 1288 23 0 0 0 0 4312 49 0 0 0 0 1288 23
+69 rmnet0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+70 rmnet0 0x0 10080 0 22266 30 20976 30 0 0 0 0 22266 30 0 0 0 0 20976 30
+71 rmnet0 0x0 10080 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/core/tests/coretests/res/raw/xt_qtaguid_typical_with_set b/core/tests/coretests/res/raw/xt_qtaguid_typical_with_set
deleted file mode 100644
index b302bb7..0000000
--- a/core/tests/coretests/res/raw/xt_qtaguid_typical_with_set
+++ /dev/null
@@ -1,13 +0,0 @@
-idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_packets rx_tcp_bytes rx_udp_packets rx_udp_bytes rx_other_packets rx_other_bytes tx_tcp_packets tx_tcp_bytes tx_udp_packets tx_udp_bytes tx_other_packets tx_other_bytes
-2 rmnet0 0x0 0 0 14855 82 2804 47 2000 45 12799 35 56 2 676 13 2128 34 0 0
-3 rmnet0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-4 rmnet0 0x0 1000 0 278102 253 10487 182 277342 243 760 10 0 0 9727 172 760 10 0 0
-5 rmnet0 0x0 1000 1 26033 30 1401 26 25881 28 152 2 0 0 1249 24 152 2 0 0
-6 rmnet0 0x0 10012 0 40524 272 134138 293 40524 272 0 0 0 0 134138 293 0 0 0 0
-7 rmnet0 0x0 10012 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-8 rmnet0 0x0 10034 0 15791 59 9905 69 15791 59 0 0 0 0 9905 69 0 0 0 0
-9 rmnet0 0x0 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-10 rmnet0 0x0 10055 0 3602 29 7739 59 3602 29 0 0 0 0 7739 59 0 0 0 0
-11 rmnet0 0x0 10055 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-12 rmnet0 0x7fff000300000000 1000 0 483 4 1931 6 483 4 0 0 0 0 1931 6 0 0 0 0
-13 rmnet0 0x7fff000300000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index 7082deb..b37eb46 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -51,6 +51,27 @@
         assertEquals(-1, stats.findIndex(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE));
     }
 
+    public void testFindIndexHinted() {
+        final NetworkStats stats = new NetworkStats(TEST_START, 3)
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 10)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 11)
+                .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 12)
+                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 1024L, 8L, 0L, 0L, 10)
+                .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 1024L, 8L, 11)
+                .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 12);
+
+        // verify that we correctly find across regardless of hinting
+        for (int hint = 0; hint < stats.size(); hint++) {
+            assertEquals(0, stats.findIndexHinted(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, hint));
+            assertEquals(1, stats.findIndexHinted(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, hint));
+            assertEquals(2, stats.findIndexHinted(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, hint));
+            assertEquals(3, stats.findIndexHinted(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, hint));
+            assertEquals(4, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, hint));
+            assertEquals(5, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, hint));
+            assertEquals(-1, stats.findIndexHinted(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, hint));
+        }
+    }
+
     public void testAddEntryGrow() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 2);
 
@@ -257,6 +278,22 @@
         assertValues(stats.getTotal(null, ifaces), 1024L, 64L, 0L, 0L, 0L);
     }
 
+    public void testWithoutUid() throws Exception {
+        final NetworkStats before = new NetworkStats(TEST_START, 3)
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 64L, 4L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 128L, 8L, 0L, 0L, 0L);
+
+        final NetworkStats after = before.withoutUid(100);
+        assertEquals(6, before.size());
+        assertEquals(2, after.size());
+        assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
+        assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 128L, 8L, 0L, 0L, 0L);
+    }
+
     private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set,
             int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
         final NetworkStats.Entry entry = stats.getValues(index, null);
diff --git a/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java b/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
index 8a64f2b..ea94fa9 100644
--- a/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -71,21 +71,12 @@
         stageFile(R.raw.xt_qtaguid_typical, new File(mTestProc, "net/xt_qtaguid/stats"));
 
         final NetworkStats stats = mFactory.readNetworkStatsDetail();
-        assertEquals(31, stats.size());
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0, 14615L, 4270L);
-        assertStatsEntry(stats, "wlan0", 10004, SET_DEFAULT, 0, 333821L, 53558L);
-        assertStatsEntry(stats, "wlan0", 10004, SET_DEFAULT, 1947740890, 18725L, 1066L);
-        assertStatsEntry(stats, "rmnet0", 10037, SET_DEFAULT, 0, 31184994L, 684122L);
-        assertStatsEntry(stats, "rmnet0", 10037, SET_DEFAULT, 1947740890, 28507378L, 437004L);
-    }
-
-    public void testNetworkStatsDetailExtended() throws Exception {
-        stageFile(R.raw.xt_qtaguid_extended, new File(mTestProc, "net/xt_qtaguid/stats"));
-
-        final NetworkStats stats = mFactory.readNetworkStatsDetail();
-        assertEquals(2, stats.size());
-        assertStatsEntry(stats, "test0", 1000, SET_DEFAULT, 0, 1024L, 2048L);
-        assertStatsEntry(stats, "test0", 1000, SET_DEFAULT, 0xF00D, 512L, 512L);
+        assertEquals(70, stats.size());
+        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 18621L, 2898L);
+        assertStatsEntry(stats, "wlan0", 10011, SET_DEFAULT, 0x0, 35777L, 5718L);
+        assertStatsEntry(stats, "wlan0", 10021, SET_DEFAULT, 0x7fffff01, 562386L, 49228L);
+        assertStatsEntry(stats, "rmnet1", 10021, SET_DEFAULT, 0x30100000, 219110L, 227423L);
+        assertStatsEntry(stats, "rmnet2", 10001, SET_DEFAULT, 0x0, 1125899906842624L, 984L);
     }
 
     public void testNetworkStatsSummary() throws Exception {
@@ -149,12 +140,12 @@
     }
 
     public void testNetworkStatsWithSet() throws Exception {
-        stageFile(R.raw.xt_qtaguid_typical_with_set, new File(mTestProc, "net/xt_qtaguid/stats"));
+        stageFile(R.raw.xt_qtaguid_typical, new File(mTestProc, "net/xt_qtaguid/stats"));
 
         final NetworkStats stats = mFactory.readNetworkStatsDetail();
-        assertEquals(12, stats.size());
-        assertStatsEntry(stats, "rmnet0", 1000, SET_DEFAULT, 0, 278102L, 253L, 10487L, 182L);
-        assertStatsEntry(stats, "rmnet0", 1000, SET_FOREGROUND, 0, 26033L, 30L, 1401L, 26L);
+        assertEquals(70, stats.size());
+        assertStatsEntry(stats, "rmnet1", 10021, SET_DEFAULT, 0x30100000, 219110L, 578L, 227423L, 676L);
+        assertStatsEntry(stats, "rmnet1", 10021, SET_FOREGROUND, 0x30100000, 742L, 3L, 1265L, 3L);
     }
 
     public void testNetworkStatsSingle() throws Exception {
diff --git a/core/tests/coretests/src/com/android/internal/util/ProcFileReaderTest.java b/core/tests/coretests/src/com/android/internal/util/ProcFileReaderTest.java
new file mode 100644
index 0000000..386a78d
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/ProcFileReaderTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.test.AndroidTestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.Charsets;
+
+/**
+ * Tests for {@link ProcFileReader}.
+ */
+public class ProcFileReaderTest extends AndroidTestCase {
+
+    public void testEmpty() throws Exception {
+        final ProcFileReader reader = buildReader("");
+
+        assertFalse(reader.hasMoreData());
+        try {
+            reader.finishLine();
+            fail("somehow finished line beyond end of stream?");
+        } catch (IOException e) {
+            // expected
+        }
+        assertFalse(reader.hasMoreData());
+    }
+
+    public void testSingleString() throws Exception {
+        final ProcFileReader reader = buildReader("a\nb\nc\n");
+
+        assertEquals("a", reader.nextString());
+        reader.finishLine();
+        assertTrue(reader.hasMoreData());
+
+        assertEquals("b", reader.nextString());
+        reader.finishLine();
+        assertTrue(reader.hasMoreData());
+
+        assertEquals("c", reader.nextString());
+        reader.finishLine();
+        assertFalse(reader.hasMoreData());
+    }
+
+    public void testMixedNumbersSkip() throws Exception {
+        final ProcFileReader reader = buildReader("1 2 3\n4 abc_def 5 6 7 8 9\n10\n");
+
+        assertEquals(1, reader.nextInt());
+        assertEquals(2, reader.nextInt());
+        assertEquals(3, reader.nextInt());
+        reader.finishLine();
+        assertTrue(reader.hasMoreData());
+
+        assertEquals(4, reader.nextInt());
+        assertEquals("abc_def", reader.nextString());
+        assertEquals(5, reader.nextInt());
+        reader.finishLine();
+        assertTrue(reader.hasMoreData());
+
+        assertEquals(10, reader.nextInt());
+        reader.finishLine();
+        assertFalse(reader.hasMoreData());
+    }
+
+    public void testBufferSize() throws Exception {
+        // read numbers using very small buffer size, exercising fillBuf()
+        final ProcFileReader reader = buildReader("1 21 3 41 5 61 7 81 9 10\n", 3);
+
+        assertEquals(1, reader.nextInt());
+        assertEquals(21, reader.nextInt());
+        assertEquals(3, reader.nextInt());
+        assertEquals(41, reader.nextInt());
+        assertEquals(5, reader.nextInt());
+        assertEquals(61, reader.nextInt());
+        assertEquals(7, reader.nextInt());
+        assertEquals(81, reader.nextInt());
+        assertEquals(9, reader.nextInt());
+        assertEquals(10, reader.nextInt());
+        reader.finishLine();
+        assertFalse(reader.hasMoreData());
+    }
+
+    public void testBlankLines() throws Exception {
+        final ProcFileReader reader = buildReader("1\n\n2\n\n3\n");
+
+        assertEquals(1, reader.nextInt());
+        reader.finishLine();
+        assertTrue(reader.hasMoreData());
+        reader.finishLine();
+        assertTrue(reader.hasMoreData());
+
+        assertEquals(2, reader.nextInt());
+        reader.finishLine();
+        assertTrue(reader.hasMoreData());
+        reader.finishLine();
+        assertTrue(reader.hasMoreData());
+
+        assertEquals(3, reader.nextInt());
+        reader.finishLine();
+        assertFalse(reader.hasMoreData());
+    }
+
+    public void testMinMax() throws Exception {
+        final ProcFileReader reader = buildReader(
+                "1 -1024 9223372036854775807 -9223372036854775808\n");
+
+        assertEquals(1, reader.nextLong());
+        assertEquals(-1024, reader.nextLong());
+        assertEquals(Long.MAX_VALUE, reader.nextLong());
+        assertEquals(Long.MIN_VALUE, reader.nextLong());
+        reader.finishLine();
+        assertFalse(reader.hasMoreData());
+    }
+
+    public void testDelimiterNeverFound() throws Exception {
+        final ProcFileReader reader = buildReader("teststringwithoutdelimiters");
+
+        try {
+            reader.nextString();
+            fail("somehow read a string value?");
+        } catch (IOException e) {
+            // expected
+            assertTrue(e.getMessage().contains("end of stream"));
+        }
+    }
+
+    public void testLargerThanBuffer() throws Exception {
+        // try finishing line larger than buffer
+        final ProcFileReader reader = buildReader("1 teststringlongerthanbuffer\n", 4);
+
+        assertEquals(1, reader.nextLong());
+        try {
+            reader.finishLine();
+            fail("somehow finished line?");
+        } catch (IOException e) {
+            // expected
+            assertTrue(e.getMessage().contains("already-full buffer"));
+        }
+    }
+
+    private static ProcFileReader buildReader(String string) throws IOException {
+        return buildReader(string, 2048);
+    }
+
+    private static ProcFileReader buildReader(String string, int bufferSize) throws IOException {
+        return new ProcFileReader(
+                new ByteArrayInputStream(string.getBytes(Charsets.US_ASCII)), bufferSize);
+    }
+}
diff --git a/docs/html/guide/developing/device.jd b/docs/html/guide/developing/device.jd
index 9ce3649..deb7a2d 100644
--- a/docs/html/guide/developing/device.jd
+++ b/docs/html/guide/developing/device.jd
@@ -58,14 +58,17 @@
 element.</p>
   </li>
   <li>Set up your device to allow installation of non-Market applications. <p>On
-the device, go to <strong>Settings > Applications</strong> and enable
+the device, go to <strong>Settings > Applications</strong> and enable 
 
-<strong>Unknown sources</strong>.</p>
+<strong>Unknown sources</strong> (on an Android 4.0 device, the setting is
+located in <strong>Settings > Security</strong>).</p>
   
   </li>
   <li>Turn on "USB Debugging" on your device.
-    <p>On the device, go to <strong>Settings > Applications > Development</strong>
-    and enable <strong>USB debugging</strong>.</p>
+    <p>On the device, go to <strong>Settings > Applications > Development</strong> 
+    and enable <strong>USB debugging</strong> 
+    (on an Android 4.0 device, the setting is 
+located in <strong>Settings > Developer options</strong>).</p>
   </li>
   <li>Set up your system to detect your device.
     <ul>
diff --git a/docs/html/resources/dashboard/opengl.jd b/docs/html/resources/dashboard/opengl.jd
index 9089937..07a0e43 100644
--- a/docs/html/resources/dashboard/opengl.jd
+++ b/docs/html/resources/dashboard/opengl.jd
@@ -57,7 +57,7 @@
 <div class="dashboard-panel">
 
 <img alt="" width="400" height="250"
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1|GL%202.0%20%26%201.1&chd=t%3A9.2,90.8" />
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1|GL%202.0%20%26%201.1&chd=t%3A9.8,90.2" />
 
 <table>
 <tr>
@@ -66,14 +66,14 @@
 </tr>
 <tr>
 <td>1.1</th>
-<td>9.2%</td>
+<td>9.8%</td>
 </tr>
 <tr>
 <td>2.0</th>
-<td>90.8%</td>
+<td>90.2%</td>
 </tr>
 </table>
 
-<p><em>Data collected during a 7-day period ending on October 3, 2011</em></p>
+<p><em>Data collected during a 7-day period ending on November 3, 2011</em></p>
 </div>
 
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index 135c6f2..8041096 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -52,7 +52,7 @@
 <div class="dashboard-panel">
 
 <img alt="" height="250" width="470"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.1,1.4,11.7,45.3,0.5,38.2,0.2,0.9,0.7&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.0|Android%203.1|Android%203.2&chco=c4df9b,6fad0c" />
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.9,1.4,10.7,40.7,0.5,43.9,0.1,0.9,0.9&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.0|Android%203.1|Android%203.2&chco=c4df9b,6fad0c" />
 
 <table>
 <tr>
@@ -61,21 +61,21 @@
   <th>API Level</th>
   <th>Distribution</th>
 </tr>
-<tr><td><a href="{@docRoot}sdk/android-1.5.html">Android 1.5</a></td><td>Cupcake</td>  <td>3</td><td>1.1%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-1.5.html">Android 1.5</a></td><td>Cupcake</td>  <td>3</td><td>0.9%</td></tr>
 <tr><td><a href="{@docRoot}sdk/android-1.6.html">Android 1.6</a></td><td>Donut</td>    <td>4</td><td>1.4%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-2.1.html">Android 2.1</a></td><td>Eclair</td>   <td>7</td><td>11.7%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-2.2.html">Android 2.2</a></td><td>Froyo</td>    <td>8</td><td>45.3%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-2.1.html">Android 2.1</a></td><td>Eclair</td>   <td>7</td><td>10.7%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-2.2.html">Android 2.2</a></td><td>Froyo</td>    <td>8</td><td>40.7%</td></tr>
 <tr><td><a href="{@docRoot}sdk/android-2.3.html">Android 2.3 -<br/>
                              Android 2.3.2</a></td><td rowspan="2">Gingerbread</td>    <td>9</td><td>0.5%</td></tr>
 <tr><td><a href="{@docRoot}sdk/android-2.3.3.html">Android 2.3.3 -<br/>
-      Android 2.3.7</a></td><!-- Gingerbread -->                                       <td>10</td><td>38.2%</td></tr>
+      Android 2.3.7</a></td><!-- Gingerbread -->                                       <td>10</td><td>43.9%</td></tr>
 <tr><td><a href="{@docRoot}sdk/android-3.0.html">Android 3.0</a></td>
-                                                   <td rowspan="3">Honeycomb</td>      <td>11</td><td>0.2%</td></tr>
+                                                   <td rowspan="3">Honeycomb</td>      <td>11</td><td>0.1%</td></tr>
 <tr><td><a href="{@docRoot}sdk/android-3.1.html">Android 3.1</a></td><!-- Honeycomb --><td>12</td><td>0.9%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-3.2.html">Android 3.2</a></td><!-- Honeycomb --><td>13</td><td>0.7%</td></tr> 
+<tr><td><a href="{@docRoot}sdk/android-3.2.html">Android 3.2</a></td><!-- Honeycomb --><td>13</td><td>0.9%</td></tr> 
 </table>
 
-<p><em>Data collected during a 14-day period ending on October 3, 2011</em></p>
+<p><em>Data collected during a 14-day period ending on November 3, 2011</em></p>
 <!--
 <p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p>
 -->
@@ -104,9 +104,9 @@
 <div class="dashboard-panel">
 
 <img alt="" height="250" width="660" style="padding:5px;background:#fff"
-src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C09/15%7C10/01%7C1%3A%7C2011%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2011%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.7,99.6,99.6,99.5,99.4,99.3,99.2,99.0,98.8,98.7,98.5,98.5,98.2|97.0,97.1,97.3,97.5,97.5,97.5,97.7,97.6,97.5,97.5,97.5,97.5,97.1|93.5,93.9,94.3,94.8,95.0,95.2,95.5,95.5,95.5,95.6,95.7,95.8,95.6|66.4,68.0,69.8,71.5,73.9,75.4,77.6,79.0,80.2,81.1,82.4,83.3,83.8|2.5,3.1,4.0,6.1,9.5,13.6,17.8,20.6,24.3,27.5,31.2,34.7,38.3|1.7,2.2,3.0,5.1,8.4,12.6,16.8,20.0,23.7,26.9,30.6,34.1,37.8&chm=b,c3df9b,0,1,0|b,b4db77,1,2,0|tAndroid 2.1,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|tAndroid 2.2,3f5e0e,3,0,15,,t::-5|b,96dd28,3,4,0|b,83c916,4,5,0|tAndroid 2.3.3,131d02,5,5,15,,t::-5|B,6fad0c,5,6,0&chg=7,25&chdl=Android 1.5|Android 1.6|Android 2.1|Android 2.2|Android 2.3|Android 2.3.3&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08" />
+src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C09/15%7C10/01%7C10/15%7C11/01%7C1%3A%7C2011%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2011%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.6,99.5,99.4,99.3,99.2,99.0,98.8,98.7,98.5,98.5,98.2,98.1,98.0|97.3,97.5,97.5,97.5,97.7,97.6,97.5,97.5,97.5,97.5,97.1,97.1,97.1|94.3,94.8,95.0,95.2,95.5,95.5,95.5,95.6,95.7,95.8,95.6,95.9,95.7|69.8,71.5,73.9,75.4,77.6,79.0,80.2,81.1,82.4,83.3,83.8,84.9,85.0|4.0,6.1,9.5,13.6,17.8,20.6,24.3,27.5,31.2,34.7,38.3,41.3,44.0|3.0,5.1,8.4,12.6,16.8,20.0,23.7,26.9,30.6,34.1,37.8,40.8,43.5&chm=b,c3df9b,0,1,0|b,b4db77,1,2,0|tAndroid 2.1,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|tAndroid 2.2,3f5e0e,3,0,15,,t::-5|b,96dd28,3,4,0|b,83c916,4,5,0|tAndroid 2.3.3,131d02,5,3,15,,t::-5|B,6fad0c,5,6,0&chg=7,25&chdl=Android 1.5|Android 1.6|Android 2.1|Android 2.2|Android 2.3|Android 2.3.3&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08" />
 
-<p><em>Last historical dataset collected during a 14-day period ending on October 3, 2011</em></p>
+<p><em>Last historical dataset collected during a 14-day period ending on November 3, 2011</em></p>
 
 
 </div><!-- end dashboard-panel -->
diff --git a/docs/html/resources/dashboard/screens.jd b/docs/html/resources/dashboard/screens.jd
index 67b47d0..ec3034d 100644
--- a/docs/html/resources/dashboard/screens.jd
+++ b/docs/html/resources/dashboard/screens.jd
@@ -60,7 +60,7 @@
 <div class="dashboard-panel">
 
 <img alt="" width="400" height="250"
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=Xlarge%20/%20mdpi|Large%20/%20ldpi|Large%20/%20mdpi|Normal%20/%20hdpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Small%20/%20hdpi|Small%20/%20ldpi&chd=t%3A2.6,0.1,3.0,71.9,0.9,17.6,2.7,1.2" />
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=Xlarge%20/%20mdpi|Large%20/%20ldpi|Large%20/%20mdpi|Normal%20/%20hdpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Small%20/%20hdpi|Small%20/%20ldpi&chd=t%3A2.9,0.1,3.1,70.8,1.0,17.7,3.0,1.3" />
 
 <table>
 <tr>
@@ -71,31 +71,31 @@
 <th scope="col">xhdpi</th>
 </tr>
 <tr><th scope="row">small</th> 
-<td>1.2%</td>     <!-- small/ldpi -->
+<td>1.3%</td>     <!-- small/ldpi -->
 <td></td>     <!-- small/mdpi -->
-<td>2.7%</td> <!-- small/hdpi -->
+<td>3.0%</td> <!-- small/hdpi -->
 <td></td>     <!-- small/xhdpi -->
 </tr> 
 <tr><th scope="row">normal</th> 
-<td>0.9%</td>  <!-- normal/ldpi -->
-<td>17.6%</td> <!-- normal/mdpi -->
-<td>71.9%</td> <!-- normal/hdpi -->
+<td>1.0%</td>  <!-- normal/ldpi -->
+<td>17.7%</td> <!-- normal/mdpi -->
+<td>70.8%</td> <!-- normal/hdpi -->
 <td></td>      <!-- normal/xhdpi -->
 </tr> 
 <tr><th scope="row">large</th> 
 <td>0.1%</td>     <!-- large/ldpi -->
-<td>3.0%</td> <!-- large/mdpi -->
+<td>3.1%</td> <!-- large/mdpi -->
 <td></td>     <!-- large/hdpi -->
 <td></td>     <!-- large/xhdpi -->
 </tr> 
 <tr><th scope="row">xlarge</th> 
 <td></td>     <!-- xlarge/ldpi -->
-<td>2.6%</td> <!-- xlarge/mdpi -->
+<td>2.9%</td> <!-- xlarge/mdpi -->
 <td></td>     <!-- xlarge/hdpi -->
 <td></td>     <!-- xlarge/xhdpi -->
 </tr> 
 </table>
 
-<p><em>Data collected during a 7-day period ending on October 3, 2011</em></p>
+<p><em>Data collected during a 7-day period ending on November 3, 2011</em></p>
 </div>
 
diff --git a/docs/html/sdk/android-4.0.jd b/docs/html/sdk/android-4.0.jd
index 2ccc927..e886bdf 100644
--- a/docs/html/sdk/android-4.0.jd
+++ b/docs/html/sdk/android-4.0.jd
@@ -518,7 +518,7 @@
 
 <p>To specify the focus or metering areas to use, simply call {@link
 android.hardware.Camera.Parameters#setFocusAreas setFocusAreas()} or {@link
-android.hardware.Camera.Parameters#setFocusAreas setMeteringAreas()}. Each take a {@link
+android.hardware.Camera.Parameters#setMeteringAreas setMeteringAreas()}. Each take a {@link
 java.util.List} of {@link android.hardware.Camera.Area} objects that indicate the areas to consider
 for focus or metering. For example, you might implement a feature that allows the user to set the
 focus area by touching an area of the preview, which you then translate to an {@link
@@ -1028,7 +1028,7 @@
 <p>Android 4.0 gives users precise visibility of how much network data their applications are using.
 The Settings app provides controls that allow users to manage set limits for network data usage and
 even disable the use of background data for individual apps. In order to avoid users disabling your
-app’s access to data from the background, you should develop strategies to use use the data
+app’s access to data from the background, you should develop strategies to use the data
 connection efficiently and adjust your usage depending on the type of connection available.</p>
 
 <p>If your application performs a lot of network transactions, you should provide user settings that
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index bc7e906..18b8bc7 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -202,18 +202,30 @@
 
     @Override
     public void setAlpha(int alpha) {
+        if (mPaint == null && alpha == 0xFF) {
+            // Fast common case -- leave at normal alpha.
+            return;
+        }
         getPaint().setAlpha(alpha);
         invalidateSelf();
     }
     
     @Override
     public void setColorFilter(ColorFilter cf) {
+        if (mPaint == null && cf == null) {
+            // Fast common case -- leave at no color filter.
+            return;
+        }
         getPaint().setColorFilter(cf);
         invalidateSelf();
     }
 
     @Override
     public void setDither(boolean dither) {
+        if (mPaint == null && dither == DEFAULT_DITHER) {
+            // Fast common case -- leave at default dither.
+            return;
+        }
         getPaint().setDither(dither);
         invalidateSelf();
     }
diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h
index dc45ff0..4f342a2 100644
--- a/include/utils/BlobCache.h
+++ b/include/utils/BlobCache.h
@@ -19,19 +19,21 @@
 
 #include <stddef.h>
 
+#include <utils/Flattenable.h>
 #include <utils/RefBase.h>
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
 
 namespace android {
 
-// A BlobCache is an in-memory cache for binary key/value pairs. All the public
-// methods are thread-safe.
+// A BlobCache is an in-memory cache for binary key/value pairs.  A BlobCache
+// does NOT provide any thread-safety guarantees.
 //
-// The cache contents can be serialized to a file and reloaded in a subsequent
-// execution of the program. This serialization is non-portable and should only
-// be loaded by the device that generated it.
-class BlobCache : public RefBase {
+// The cache contents can be serialized to an in-memory buffer or mmap'd file
+// and then reloaded in a subsequent execution of the program.  This
+// serialization is non-portable and the data should only be used by the device
+// that generated it.
+class BlobCache : public RefBase, public Flattenable {
 public:
 
     // Create an empty blob cache. The blob cache will cache key/value pairs
@@ -58,14 +60,13 @@
     void set(const void* key, size_t keySize, const void* value,
             size_t valueSize);
 
-    // The get function retrieves from the cache the binary value associated
-    // with a given binary key.  If the key is present in the cache then the
-    // length of the binary value associated with that key is returned.  If the
-    // value argument is non-NULL and the size of the cached value is less than
-    // valueSize bytes then the cached value is copied into the buffer pointed
-    // to by the value argument.  If the key is not present in the cache then 0
-    // is returned and the buffer pointed to by the value argument is not
-    // modified.
+    // get retrieves from the cache the binary value associated with a given
+    // binary key.  If the key is present in the cache then the length of the
+    // binary value associated with that key is returned.  If the value argument
+    // is non-NULL and the size of the cached value is less than valueSize bytes
+    // then the cached value is copied into the buffer pointed to by the value
+    // argument.  If the key is not present in the cache then 0 is returned and
+    // the buffer pointed to by the value argument is not modified.
     //
     // Note that when calling get multiple times with the same key, the later
     // calls may fail, returning 0, even if earlier calls succeeded.  The return
@@ -77,6 +78,37 @@
     //   0 <= valueSize
     size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
 
+    // getFlattenedSize returns the number of bytes needed to store the entire
+    // serialized cache.
+    virtual size_t getFlattenedSize() const;
+
+    // getFdCount returns the number of file descriptors that will result from
+    // flattening the cache.  This will always return 0 so as to allow the
+    // flattened cache to be saved to disk and then later restored.
+    virtual size_t getFdCount() const;
+
+    // flatten serializes the current contents of the cache into the memory
+    // pointed to by 'buffer'.  The serialized cache contents can later be
+    // loaded into a BlobCache object using the unflatten method.  The contents
+    // of the BlobCache object will not be modified.
+    //
+    // Preconditions:
+    //   size >= this.getFlattenedSize()
+    //   count == 0
+    virtual status_t flatten(void* buffer, size_t size, int fds[],
+            size_t count) const;
+
+    // unflatten replaces the contents of the cache with the serialized cache
+    // contents in the memory pointed to by 'buffer'.  The previous contents of
+    // the BlobCache will be evicted from the cache.  If an error occurs while
+    // unflattening the serialized cache contents then the BlobCache will be
+    // left in an empty state.
+    //
+    // Preconditions:
+    //   count == 0
+    virtual status_t unflatten(void const* buffer, size_t size, int fds[],
+            size_t count);
+
 private:
     // Copying is disallowed.
     BlobCache(const BlobCache&);
@@ -144,6 +176,46 @@
         sp<Blob> mValue;
     };
 
+    // A Header is the header for the entire BlobCache serialization format. No
+    // need to make this portable, so we simply write the struct out.
+    struct Header {
+        // mMagicNumber is the magic number that identifies the data as
+        // serialized BlobCache contents.  It must always contain 'Blb$'.
+        uint32_t mMagicNumber;
+
+        // mBlobCacheVersion is the serialization format version.
+        uint32_t mBlobCacheVersion;
+
+        // mDeviceVersion is the device-specific version of the cache.  This can
+        // be used to invalidate the cache.
+        uint32_t mDeviceVersion;
+
+        // mNumEntries is number of cache entries following the header in the
+        // data.
+        size_t mNumEntries;
+    };
+
+    // An EntryHeader is the header for a serialized cache entry.  No need to
+    // make this portable, so we simply write the struct out.  Each EntryHeader
+    // is followed imediately by the key data and then the value data.
+    //
+    // The beginning of each serialized EntryHeader is 4-byte aligned, so the
+    // number of bytes that a serialized cache entry will occupy is:
+    //
+    //   ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3
+    //
+    struct EntryHeader {
+        // mKeySize is the size of the entry key in bytes.
+        size_t mKeySize;
+
+        // mValueSize is the size of the entry value in bytes.
+        size_t mValueSize;
+
+        // mData contains both the key and value data for the cache entry.  The
+        // key comes first followed immediately by the value.
+        uint8_t mData[];
+    };
+
     // mMaxKeySize is the maximum key size that will be cached. Calls to
     // BlobCache::set with a keySize parameter larger than mMaxKeySize will
     // simply not add the key/value pair to the cache.
@@ -166,17 +238,12 @@
     size_t mTotalSize;
 
     // mRandState is the pseudo-random number generator state. It is passed to
-    // nrand48 to generate random numbers when needed. It must be protected by
-    // mMutex.
+    // nrand48 to generate random numbers when needed.
     unsigned short mRandState[3];
 
     // mCacheEntries stores all the cache entries that are resident in memory.
     // Cache entries are added to it by the 'set' method.
     SortedVector<CacheEntry> mCacheEntries;
-
-    // mMutex is used to synchronize access to all member variables.  It must be
-    // locked any time the member variables are written or read.
-    Mutex mMutex;
 };
 
 }
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index c72a45b..6f84206 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -116,7 +116,7 @@
     // Choose a name using the PID and a process-unique ID.
     mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
 
-    ST_LOGV("SurfaceTexture::SurfaceTexture");
+    ST_LOGV("SurfaceTexture");
     sp<ISurfaceComposer> composer(ComposerService::getComposerService());
     mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
     mNextCrop.makeInvalid();
@@ -125,7 +125,7 @@
 }
 
 SurfaceTexture::~SurfaceTexture() {
-    ST_LOGV("SurfaceTexture::~SurfaceTexture");
+    ST_LOGV("~SurfaceTexture");
     freeAllBuffersLocked();
 }
 
@@ -169,7 +169,7 @@
 }
 
 status_t SurfaceTexture::setBufferCount(int bufferCount) {
-    ST_LOGV("SurfaceTexture::setBufferCount");
+    ST_LOGV("setBufferCount: count=%d", bufferCount);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -217,6 +217,7 @@
 
 status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
 {
+    ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h);
     if (!w || !h) {
         ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)",
                 w, h);
@@ -230,7 +231,7 @@
 }
 
 status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
-    ST_LOGV("SurfaceTexture::requestBuffer");
+    ST_LOGV("requestBuffer: slot=%d", slot);
     Mutex::Autolock lock(mMutex);
     if (mAbandoned) {
         ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!");
@@ -248,7 +249,7 @@
 
 status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
         uint32_t format, uint32_t usage) {
-    ST_LOGV("SurfaceTexture::dequeueBuffer");
+    ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage);
 
     if ((w && !h) || (!w && h)) {
         ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
@@ -342,6 +343,8 @@
         // clients are not allowed to dequeue more than one buffer
         // if they didn't set a buffer count.
         if (!mClientBufferCount && dequeuedCount) {
+            ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without "
+                    "setting the buffer count");
             return -EINVAL;
         }
 
@@ -375,6 +378,8 @@
     }
 
     if (found == INVALID_BUFFER_SLOT) {
+        // This should not happen.
+        ST_LOGE("dequeueBuffer: no available buffer slots");
         return -EBUSY;
     }
 
@@ -427,10 +432,13 @@
         }
         returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
     }
+    ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", buf,
+            mSlots[buf].mGraphicBuffer->handle, returnFlags);
     return returnFlags;
 }
 
 status_t SurfaceTexture::setSynchronousMode(bool enabled) {
+    ST_LOGV("setSynchronousMode: enabled=%d", enabled);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -462,7 +470,7 @@
 
 status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp,
         uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
-    ST_LOGV("SurfaceTexture::queueBuffer");
+    ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp);
 
     sp<FrameAvailableListener> listener;
 
@@ -534,7 +542,7 @@
 }
 
 void SurfaceTexture::cancelBuffer(int buf) {
-    ST_LOGV("SurfaceTexture::cancelBuffer");
+    ST_LOGV("cancelBuffer: slot=%d", buf);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -556,7 +564,9 @@
 }
 
 status_t SurfaceTexture::setCrop(const Rect& crop) {
-    ST_LOGV("SurfaceTexture::setCrop");
+    ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right,
+            crop.bottom);
+
     Mutex::Autolock lock(mMutex);
     if (mAbandoned) {
         ST_LOGE("setCrop: SurfaceTexture has been abandoned!");
@@ -567,7 +577,7 @@
 }
 
 status_t SurfaceTexture::setTransform(uint32_t transform) {
-    ST_LOGV("SurfaceTexture::setTransform");
+    ST_LOGV("setTransform: xform=%#x", transform);
     Mutex::Autolock lock(mMutex);
     if (mAbandoned) {
         ST_LOGE("setTransform: SurfaceTexture has been abandoned!");
@@ -579,7 +589,7 @@
 
 status_t SurfaceTexture::connect(int api,
         uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
-    ST_LOGV("SurfaceTexture::connect(this=%p, %d)", this, api);
+    ST_LOGV("connect: api=%d", api);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -612,7 +622,7 @@
 }
 
 status_t SurfaceTexture::disconnect(int api) {
-    ST_LOGV("SurfaceTexture::disconnect(this=%p, %d)", this, api);
+    ST_LOGV("disconnect: api=%d", api);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -640,6 +650,7 @@
             }
             break;
         default:
+            ST_LOGE("disconnect: unknown API %d", api);
             err = -EINVAL;
             break;
     }
@@ -647,13 +658,14 @@
 }
 
 status_t SurfaceTexture::setScalingMode(int mode) {
-    ST_LOGV("SurfaceTexture::setScalingMode(%d)", mode);
+    ST_LOGV("setScalingMode: mode=%d", mode);
 
     switch (mode) {
         case NATIVE_WINDOW_SCALING_MODE_FREEZE:
         case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
             break;
         default:
+            ST_LOGE("unknown scaling mode: %d", mode);
             return BAD_VALUE;
     }
 
@@ -663,7 +675,7 @@
 }
 
 status_t SurfaceTexture::updateTexImage() {
-    ST_LOGV("SurfaceTexture::updateTexImage");
+    ST_LOGV("updateTexImage");
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -713,6 +725,10 @@
             return -EINVAL;
         }
 
+        ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
+                mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf,
+                mSlots[buf].mGraphicBuffer->handle);
+
         if (mCurrentTexture != INVALID_BUFFER_SLOT) {
             // The current buffer becomes FREE if it was still in the queued
             // state. If it has already been given to the client
@@ -771,7 +787,7 @@
 }
 
 void SurfaceTexture::computeCurrentTransformMatrix() {
-    ST_LOGV("SurfaceTexture::computeCurrentTransformMatrix");
+    ST_LOGV("computeCurrentTransformMatrix");
 
     float xform[16];
     for (int i = 0; i < 16; i++) {
@@ -862,14 +878,14 @@
 }
 
 nsecs_t SurfaceTexture::getTimestamp() {
-    ST_LOGV("SurfaceTexture::getTimestamp");
+    ST_LOGV("getTimestamp");
     Mutex::Autolock lock(mMutex);
     return mCurrentTimestamp;
 }
 
 void SurfaceTexture::setFrameAvailableListener(
         const sp<FrameAvailableListener>& listener) {
-    ST_LOGV("SurfaceTexture::setFrameAvailableListener");
+    ST_LOGV("setFrameAvailableListener");
     Mutex::Autolock lock(mMutex);
     mFrameAvailableListener = listener;
 }
diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp
index 590576a..d38aae9 100644
--- a/libs/utils/BlobCache.cpp
+++ b/libs/utils/BlobCache.cpp
@@ -21,10 +21,20 @@
 #include <string.h>
 
 #include <utils/BlobCache.h>
+#include <utils/Errors.h>
 #include <utils/Log.h>
 
 namespace android {
 
+// BlobCache::Header::mMagicNumber value
+static const uint32_t blobCacheMagic = '_Bb$';
+
+// BlobCache::Header::mBlobCacheVersion value
+static const uint32_t blobCacheVersion = 1;
+
+// BlobCache::Header::mDeviceVersion value
+static const uint32_t blobCacheDeviceVersion = 1;
+
 BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
         mMaxKeySize(maxKeySize),
         mMaxValueSize(maxValueSize),
@@ -67,12 +77,10 @@
         return;
     }
 
-    Mutex::Autolock lock(mMutex);
     sp<Blob> dummyKey(new Blob(key, keySize, false));
     CacheEntry dummyEntry(dummyKey, NULL);
 
     while (true) {
-
         ssize_t index = mCacheEntries.indexOf(dummyEntry);
         if (index < 0) {
             // Create a new cache entry.
@@ -129,7 +137,6 @@
                 keySize, mMaxKeySize);
         return 0;
     }
-    Mutex::Autolock lock(mMutex);
     sp<Blob> dummyKey(new Blob(key, keySize, false));
     CacheEntry dummyEntry(dummyKey, NULL);
     ssize_t index = mCacheEntries.indexOf(dummyEntry);
@@ -152,6 +159,133 @@
     return valueBlobSize;
 }
 
+static inline size_t align4(size_t size) {
+    return (size + 3) & ~3;
+}
+
+size_t BlobCache::getFlattenedSize() const {
+    size_t size = sizeof(Header);
+    for (size_t i = 0; i < mCacheEntries.size(); i++) {
+        const CacheEntry& e(mCacheEntries[i]);
+        sp<Blob> keyBlob = e.getKey();
+        sp<Blob> valueBlob = e.getValue();
+        size = align4(size);
+        size += sizeof(EntryHeader) + keyBlob->getSize() +
+                valueBlob->getSize();
+    }
+    return size;
+}
+
+size_t BlobCache::getFdCount() const {
+    return 0;
+}
+
+status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count)
+        const {
+    if (count != 0) {
+        LOGE("flatten: nonzero fd count: %d", count);
+        return BAD_VALUE;
+    }
+
+    // Write the cache header
+    if (size < sizeof(Header)) {
+        LOGE("flatten: not enough room for cache header");
+        return BAD_VALUE;
+    }
+    Header* header = reinterpret_cast<Header*>(buffer);
+    header->mMagicNumber = blobCacheMagic;
+    header->mBlobCacheVersion = blobCacheVersion;
+    header->mDeviceVersion = blobCacheDeviceVersion;
+    header->mNumEntries = mCacheEntries.size();
+
+    // Write cache entries
+    uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
+    off_t byteOffset = align4(sizeof(Header));
+    for (size_t i = 0; i < mCacheEntries.size(); i++) {
+        const CacheEntry& e(mCacheEntries[i]);
+        sp<Blob> keyBlob = e.getKey();
+        sp<Blob> valueBlob = e.getValue();
+        size_t keySize = keyBlob->getSize();
+        size_t valueSize = valueBlob->getSize();
+
+        size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+        if (byteOffset + entrySize > size) {
+            LOGE("flatten: not enough room for cache entries");
+            return BAD_VALUE;
+        }
+
+        EntryHeader* eheader = reinterpret_cast<EntryHeader*>(
+            &byteBuffer[byteOffset]);
+        eheader->mKeySize = keySize;
+        eheader->mValueSize = valueSize;
+
+        memcpy(eheader->mData, keyBlob->getData(), keySize);
+        memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize);
+
+        byteOffset += align4(entrySize);
+    }
+
+    return OK;
+}
+
+status_t BlobCache::unflatten(void const* buffer, size_t size, int fds[],
+        size_t count) {
+    // All errors should result in the BlobCache being in an empty state.
+    mCacheEntries.clear();
+
+    if (count != 0) {
+        LOGE("unflatten: nonzero fd count: %d", count);
+        return BAD_VALUE;
+    }
+
+    // Read the cache header
+    if (size < sizeof(Header)) {
+        LOGE("unflatten: not enough room for cache header");
+        return BAD_VALUE;
+    }
+    const Header* header = reinterpret_cast<const Header*>(buffer);
+    if (header->mMagicNumber != blobCacheMagic) {
+        LOGE("unflatten: bad magic number: %d", header->mMagicNumber);
+        return BAD_VALUE;
+    }
+    if (header->mBlobCacheVersion != blobCacheVersion ||
+            header->mDeviceVersion != blobCacheDeviceVersion) {
+        // We treat version mismatches as an empty cache.
+        return OK;
+    }
+
+    // Read cache entries
+    const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer);
+    off_t byteOffset = align4(sizeof(Header));
+    size_t numEntries = header->mNumEntries;
+    for (size_t i = 0; i < numEntries; i++) {
+        if (byteOffset + sizeof(EntryHeader) > size) {
+            mCacheEntries.clear();
+            LOGE("unflatten: not enough room for cache entry headers");
+            return BAD_VALUE;
+        }
+
+        const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
+                &byteBuffer[byteOffset]);
+        size_t keySize = eheader->mKeySize;
+        size_t valueSize = eheader->mValueSize;
+        size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+
+        if (byteOffset + entrySize > size) {
+            mCacheEntries.clear();
+            LOGE("unflatten: not enough room for cache entry headers");
+            return BAD_VALUE;
+        }
+
+        const uint8_t* data = eheader->mData;
+        set(data, keySize, data + keySize, valueSize);
+
+        byteOffset += align4(entrySize);
+    }
+
+    return OK;
+}
+
 long int BlobCache::blob_random() {
 #ifdef _WIN32
     return rand();
@@ -179,7 +313,7 @@
         mData(copyData ? malloc(size) : data),
         mSize(size),
         mOwnsData(copyData) {
-    if (copyData) {
+    if (data != NULL && copyData) {
         memcpy(const_cast<void*>(mData), data, size);
     }
 }
diff --git a/libs/utils/tests/BlobCache_test.cpp b/libs/utils/tests/BlobCache_test.cpp
index 653ea5e..b64cc39 100644
--- a/libs/utils/tests/BlobCache_test.cpp
+++ b/libs/utils/tests/BlobCache_test.cpp
@@ -14,9 +14,13 @@
  ** limitations under the License.
  */
 
+#include <fcntl.h>
+#include <stdio.h>
+
 #include <gtest/gtest.h>
 
 #include <utils/BlobCache.h>
+#include <utils/Errors.h>
 
 namespace android {
 
@@ -254,4 +258,164 @@
     ASSERT_EQ(maxEntries/2 + 1, numCached);
 }
 
+class BlobCacheFlattenTest : public BlobCacheTest {
+protected:
+    virtual void SetUp() {
+        BlobCacheTest::SetUp();
+        mBC2 = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
+    }
+
+    virtual void TearDown() {
+        mBC2.clear();
+        BlobCacheTest::TearDown();
+    }
+
+    void roundTrip() {
+        size_t size = mBC->getFlattenedSize();
+        uint8_t* flat = new uint8_t[size];
+        ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+        ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+        delete[] flat;
+    }
+
+    sp<BlobCache> mBC2;
+};
+
+TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    roundTrip();
+    ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
+    ASSERT_EQ('e', buf[0]);
+    ASSERT_EQ('f', buf[1]);
+    ASSERT_EQ('g', buf[2]);
+    ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenFullCache) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    roundTrip();
+
+    // Verify the deserialized cache
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        uint8_t v = 0xee;
+        ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1));
+        ASSERT_EQ(k, v);
+    }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+    delete[] flat;
+
+    // Verify the cache that we just serialized
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        uint8_t v = 0xee;
+        ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1));
+        ASSERT_EQ(k, v);
+    }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    size_t size = mBC->getFlattenedSize() - 1;
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size, NULL, 0));
+    delete[] flat;
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+    flat[1] = ~flat[1];
+
+    // Bad magic should cause an error.
+    ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size, NULL, 0));
+    delete[] flat;
+
+    // The error should cause the unflatten to result in an empty cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+    flat[5] = ~flat[5];
+
+    // Version mismatches shouldn't cause errors, but should not use the
+    // serialized entries
+    ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+    delete[] flat;
+
+    // The version mismatch should cause the unflatten to result in an empty
+    // cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+    flat[10] = ~flat[10];
+
+    // Version mismatches shouldn't cause errors, but should not use the
+    // serialized entries
+    ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+    delete[] flat;
+
+    // The version mismatch should cause the unflatten to result in an empty
+    // cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+
+    // A buffer truncation shouldt cause an error
+    ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1, NULL, 0));
+    delete[] flat;
+
+    // The error should cause the unflatten to result in an empty cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
 } // namespace android
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index b88296f..09152f5 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -56,6 +56,53 @@
     return OK;
 }
 
+// stolen from dalvik/vm/checkJni.cpp
+static bool isValidUtf8(const char* bytes) {
+    while (*bytes != '\0') {
+        unsigned char utf8 = *(bytes++);
+        // Switch on the high four bits.
+        switch (utf8 >> 4) {
+        case 0x00:
+        case 0x01:
+        case 0x02:
+        case 0x03:
+        case 0x04:
+        case 0x05:
+        case 0x06:
+        case 0x07:
+            // Bit pattern 0xxx. No need for any extra bytes.
+            break;
+        case 0x08:
+        case 0x09:
+        case 0x0a:
+        case 0x0b:
+        case 0x0f:
+            /*
+             * Bit pattern 10xx or 1111, which are illegal start bytes.
+             * Note: 1111 is valid for normal UTF-8, but not the
+             * modified UTF-8 used here.
+             */
+            return false;
+        case 0x0e:
+            // Bit pattern 1110, so there are two additional bytes.
+            utf8 = *(bytes++);
+            if ((utf8 & 0xc0) != 0x80) {
+                return false;
+            }
+            // Fall through to take care of the final byte.
+        case 0x0c:
+        case 0x0d:
+            // Bit pattern 110x, so there is one additional byte.
+            utf8 = *(bytes++);
+            if ((utf8 & 0xc0) != 0x80) {
+                return false;
+            }
+            break;
+        }
+    }
+    return true;
+}
+
 class MyMediaScannerClient : public MediaScannerClient
 {
 public:
@@ -123,7 +170,22 @@
             mEnv->ExceptionClear();
             return NO_MEMORY;
         }
-        if ((valueStr = mEnv->NewStringUTF(value)) == NULL) {
+        char *cleaned = NULL;
+        if (!isValidUtf8(value)) {
+            cleaned = strdup(value);
+            char *chp = cleaned;
+            char ch;
+            while ((ch = *chp)) {
+                if (ch & 0x80) {
+                    *chp = '?';
+                }
+                chp++;
+            }
+            value = cleaned;
+        }
+        valueStr = mEnv->NewStringUTF(value);
+        free(cleaned);
+        if (valueStr == NULL) {
             mEnv->DeleteLocalRef(nameStr);
             mEnv->ExceptionClear();
             return NO_MEMORY;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 7cdb76c..70208f8 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -282,7 +282,7 @@
 
                 if (err == -EWOULDBLOCK) {
                     if (mSource->feedMoreTSData() == OK) {
-                        msg->post();
+                        msg->post(10000ll);
                     }
                 }
             } else if (what == ACodec::kWhatEOS) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
index e3aa8cf..1fa5c0d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
@@ -44,13 +44,7 @@
  */
 public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
     private String TAG = "MediaBassBoostTest";
-    private final static int MIN_ENERGY_RATIO_2 = 3;
     private final static short TEST_STRENGTH = 500;
-    private final static int TEST_VOLUME = 4;
-    // Implementor UUID for volume controller effect defined in
-    // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
-    private final static UUID VOLUME_EFFECT_UUID =
-        UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b");
 
     private BassBoost mBassBoost = null;
     private int mSession = -1;
@@ -184,85 +178,6 @@
     }
 
     //-----------------------------------------------------------------
-    // 2 - Effect action
-    //----------------------------------
-
-    //Test case 2.0: test actual bass boost influence on sound
-    @LargeTest
-    public void test2_0SoundModification() throws Exception {
-        boolean result = false;
-        String msg = "test2_0SoundModification()";
-        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,
-                           TEST_VOLUME,
-                           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,
-                                VOLUME_EFFECT_UUID,
-                                0,
-                                0);
-            vc.setEnabled(true);
-
-            mp = new MediaPlayer();
-            mp.setDataSource(MediaNames.SINE_200_1000);
-            mp.setLooping(true);
-            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
-            getBassBoost(mp.getAudioSessionId());
-            mp.prepare();
-            mp.start();
-            Thread.sleep(200);
-            // measure reference energy around 1kHz
-            int refEnergy200 = probe.capture(200);
-            int refEnergy1000 = probe.capture(1000);
-            mBassBoost.setStrength((short)1000);
-            mBassBoost.setEnabled(true);
-            Thread.sleep(4000);
-            // measure energy around 1kHz with band level at min
-            int energy200 = probe.capture(200);
-            int energy1000 = probe.capture(1000);
-            // verify that the energy ration between low and high frequencies is at least
-            // MIN_ENERGY_RATIO_2 times higher with bassboost on.
-            assertTrue(msg + ": bass boost has no effect",
-                    ((float)energy200/(float)energy1000) >
-                    (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000)));
-            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 {
-            releaseBassBoost();
-            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
     //----------------------------------
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
index ee91bbb..da9089d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
@@ -49,11 +49,6 @@
     private final static int MAX_BAND_LEVEL = 1500;
     private final static int TEST_FREQUENCY_MILLIHERTZ = 1000000;
     private final static int MIN_NUMBER_OF_PRESETS = 4;
-    private final static int TEST_VOLUME = 4;
-    // Implementor UUID for volume controller effect defined in
-    // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
-    private final static UUID VOLUME_EFFECT_UUID =
-        UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b");
 
     private Equalizer mEqualizer = null;
     private int mSession = -1;
@@ -252,80 +247,6 @@
     }
 
     //-----------------------------------------------------------------
-    // 2 - Effect action
-    //----------------------------------
-
-    //Test case 2.0: test that the equalizer actually alters the sound
-    @LargeTest
-    public void test2_0SoundModification() throws Exception {
-        boolean result = false;
-        String msg = "test2_0SoundModification()";
-        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,
-                           TEST_VOLUME,
-                           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,
-                                VOLUME_EFFECT_UUID,
-                                0,
-                                0);
-            vc.setEnabled(true);
-
-            mp = new MediaPlayer();
-            mp.setDataSource(MediaNames.SINE_200_1000);
-            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
-            getEqualizer(mp.getAudioSessionId());
-            mp.prepare();
-            mp.start();
-            Thread.sleep(500);
-            // measure reference energy around 1kHz
-            int refEnergy = probe.capture(1000);
-            short band = mEqualizer.getBand(1000000);
-            short[] levelRange = mEqualizer.getBandLevelRange();
-            mEqualizer.setBandLevel(band, levelRange[0]);
-            mEqualizer.setEnabled(true);
-            Thread.sleep(500);
-            // measure energy around 1kHz with band level at min
-            int energy = probe.capture(1000);
-            assertTrue(msg + ": equalizer has no effect at 1kHz", energy < refEnergy/4);
-            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 {
-            releaseEqualizer();
-            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
     //----------------------------------
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
index b74e525..122545f 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
@@ -44,13 +44,7 @@
  */
 public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
     private String TAG = "MediaVirtualizerTest";
-    private final static int MIN_ENERGY_RATIO_2 = 2;
     private final static short TEST_STRENGTH = 500;
-    private final static int TEST_VOLUME = 4;
-    // Implementor UUID for volume controller effect defined in
-    // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
-    private final static UUID VOLUME_EFFECT_UUID =
-        UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b");
 
     private Virtualizer mVirtualizer = null;
     private int mSession = -1;
@@ -185,89 +179,6 @@
     }
 
     //-----------------------------------------------------------------
-    // 2 - Effect action
-    //----------------------------------
-
-    //Test case 2.0: test actual virtualizer influence on sound
-    @LargeTest
-    public void test2_0SoundModification() throws Exception {
-        boolean result = false;
-        String msg = "test2_0SoundModification()";
-        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,
-                           TEST_VOLUME,
-                           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,
-                                VOLUME_EFFECT_UUID,
-                                0,
-                                0);
-            vc.setEnabled(true);
-
-            mp = new MediaPlayer();
-            mp.setDataSource(MediaNames.SINE_200_1000);
-            mp.setLooping(true);
-            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
-            getVirtualizer(mp.getAudioSessionId());
-            mp.prepare();
-            mp.start();
-            Thread.sleep(200);
-            // measure reference energy around 1kHz
-            int refEnergy200 = probe.capture(200);
-            int refEnergy1000 = probe.capture(1000);
-            mVirtualizer.setStrength((short)1000);
-            mVirtualizer.setEnabled(true);
-            Thread.sleep(4000);
-            // measure energy around 1kHz with band level at min
-            int energy200 = probe.capture(200);
-            int energy1000 = probe.capture(1000);
-            // verify that the energy ration between low and high frequencies is at least
-            // 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
-            // when output. However, we cannot capture stereo with the visualizer.
-            assertTrue(msg + ": virtualizer has no effect",
-                    ((float)energy200/(float)energy1000) >
-                    (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000)));
-            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 {
-            releaseVirtualizer();
-            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
     //----------------------------------
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index ce390a0..ff67a7e 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -26,7 +26,7 @@
     <drawable name="status_bar_recents_app_thumbnail_background">#88000000</drawable>
     <color name="status_bar_recents_app_label_color">#ffffffff</color>
     <drawable name="status_bar_notification_row_background_color">#ff090909</drawable>
-    <drawable name="notification_header_bg">#d8000000</drawable>
+    <drawable name="notification_header_bg">#FF000000</drawable>
     <drawable name="notification_tracking_bg">#d8000000</drawable>
     <color name="notification_list_shadow_top">#80000000</color>
     <drawable name="recents_callout_line">#99ffffff</drawable>
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 0a77654..7d97246 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -389,8 +389,8 @@
     boolean mLockScreenTimerActive;
 
     // visual screen saver support
-    int mScreenSaverTimeout;
-    boolean mScreenSaverEnabled = false;
+    int mScreenSaverTimeout = 0;
+    boolean mScreenSaverEnabled = true;
 
     // Behavior of ENDCALL Button.  (See Settings.System.END_BUTTON_BEHAVIOR.)
     int mEndcallBehavior;
@@ -454,7 +454,7 @@
                     Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
             resolver.registerContentObserver(Settings.System.getUriFor(
                     "fancy_rotation_anim"), false, this);
-            resolver.registerContentObserver(Settings.System.getUriFor(
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.DREAM_TIMEOUT), false, this);
             updateSettings();
         }
@@ -909,9 +909,8 @@
                 updateRotation = true;
             }
 
-            mScreenSaverTimeout = Settings.System.getInt(resolver,
+            mScreenSaverTimeout = Settings.Secure.getInt(resolver,
                     Settings.Secure.DREAM_TIMEOUT, 0);
-            mScreenSaverEnabled = true;
             updateScreenSaverTimeoutLocked();
         }
         if (updateRotation) {
@@ -3417,70 +3416,59 @@
             }
         }
 
-        // Turn this off for now, screen savers not currently enabled.
-        if (false) {
-            synchronized (mLock) {
-                updateScreenSaverTimeoutLocked();
-            }
+        synchronized (mLock) {
+            // Only posts messages; holds no additional locks.
+            updateScreenSaverTimeoutLocked();
         }
     }
 
-    Runnable mScreenSaverActivator = null;
-    /*new Runnable() {
+    Runnable mScreenSaverActivator = new Runnable() {
         public void run() {
-            synchronized (this) {
-                if (!(mScreenSaverEnabled && mScreenOn)) {
-                    Log.w(TAG, "mScreenSaverActivator ran, but the screensaver should not be showing. Who's driving this thing?");
-                    return;
-                }
+            if (!(mScreenSaverEnabled && mScreenOnEarly)) {
+                Log.w(TAG, "mScreenSaverActivator ran, but the screensaver should not be showing. Who's driving this thing?");
+                return;
+            }
 
-                if (localLOGV) Log.v(TAG, "mScreenSaverActivator entering dreamland");
-                try {
-                    String component = Settings.System.getString(
-                            mContext.getContentResolver(), Settings.Secure.DREAM_COMPONENT);
-                    if (component != null) {
-                        ComponentName cn = ComponentName.unflattenFromString(component);
-                        Intent intent = new Intent(Intent.ACTION_MAIN)
-                            .setComponent(cn)
-                            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                                | Intent.FLAG_ACTIVITY_NO_USER_ACTION
-                                | Intent.FLAG_ACTIVITY_SINGLE_TOP);
-                        mContext.startActivity(intent);
-                    } else {
-                        Log.e(TAG, "Couldn't start screen saver: none selected");
-                    }
-                } catch (android.content.ActivityNotFoundException exc) {
-                    // no screensaver? give up
-                    Log.e(TAG, "Couldn't start screen saver: none installed");
+            if (localLOGV) Log.v(TAG, "mScreenSaverActivator entering dreamland");
+            try {
+                String component = Settings.Secure.getString(
+                        mContext.getContentResolver(), Settings.Secure.DREAM_COMPONENT);
+                if (component != null) {
+                    ComponentName cn = ComponentName.unflattenFromString(component);
+                    Intent intent = new Intent(Intent.ACTION_MAIN)
+                        .setComponent(cn)
+                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                            | Intent.FLAG_ACTIVITY_NO_USER_ACTION
+                            );
+                    mContext.startActivity(intent);
+                } else {
+                    Log.e(TAG, "Couldn't start screen saver: none selected");
                 }
+            } catch (android.content.ActivityNotFoundException exc) {
+                // no screensaver? give up
+                Log.e(TAG, "Couldn't start screen saver: none installed");
             }
         }
     };
-    */
 
     // Must call while holding mLock
     private void updateScreenSaverTimeoutLocked() {
         if (mScreenSaverActivator == null) return;
 
-        // GAH...  acquiring a lock within a lock?  Please let's fix this.
-        // (Also note this is called from userActivity, with the power manager
-        // lock  held.  Not good.)
-        synchronized (mScreenSaverActivator) {
-            mHandler.removeCallbacks(mScreenSaverActivator);
-            if (mScreenSaverEnabled && mScreenOnEarly && mScreenSaverTimeout > 0) {
-                if (localLOGV)
-                    Log.v(TAG, "scheduling screensaver for " + mScreenSaverTimeout + "ms from now");
-                mHandler.postDelayed(mScreenSaverActivator, mScreenSaverTimeout);
-            } else {
-                if (localLOGV) {
-                    if (mScreenSaverTimeout == 0)
-                        Log.v(TAG, "screen saver disabled by user");
-                    else if (!mScreenOnEarly)
-                        Log.v(TAG, "screen saver disabled while screen off");
-                    else
-                        Log.v(TAG, "screen saver disabled by wakelock");
-                }
+        mHandler.removeCallbacks(mScreenSaverActivator);
+        if (mScreenSaverEnabled && mScreenOnEarly && mScreenSaverTimeout > 0) {
+            if (localLOGV)
+                Log.v(TAG, "scheduling screensaver for " + mScreenSaverTimeout + "ms from now");
+            mHandler.postDelayed(mScreenSaverActivator, mScreenSaverTimeout);
+        } else {
+            if (localLOGV) {
+                if (mScreenSaverTimeout == 0)
+                    Log.v(TAG, "screen saver disabled by user");
+                else if (!mScreenOnEarly)
+                    Log.v(TAG, "screen saver disabled while screen off");
+                else
+                    Log.v(TAG, "screen saver disabled by wakelock");
             }
         }
     }
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index eb75ebc..2af5103 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -751,10 +751,13 @@
                 return;
             }
             ArrayList<AppWidgetId> instances = p.instances;
+            final int callingUid = getCallingUid();
             final int N = instances.size();
             for (int i=0; i<N; i++) {
                 AppWidgetId id = instances.get(i);
-                updateAppWidgetInstanceLocked(id, views);
+                if (canAccessAppWidgetId(id, callingUid)) {
+                    updateAppWidgetInstanceLocked(id, views);
+                }
             }
         }
     }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index f8a7d6a..0c42926 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -293,7 +293,7 @@
     /**
      * Historical data of past broadcasts, for debugging.
      */
-    static final int MAX_BROADCAST_HISTORY = 100;
+    static final int MAX_BROADCAST_HISTORY = 25;
     final BroadcastRecord[] mBroadcastHistory
             = new BroadcastRecord[MAX_BROADCAST_HISTORY];
 
@@ -408,6 +408,12 @@
     ProcessRecord mHomeProcess;
     
     /**
+     * This is the process holding the activity the user last visited that
+     * is in a different process from the one they are currently in.
+     */
+    ProcessRecord mPreviousProcess;
+    
+    /**
      * Packages that the user has asked to have run in screen size
      * compatibility mode instead of filling the screen.
      */
@@ -8114,6 +8120,7 @@
 
         pw.println();
         pw.println("  mHomeProcess: " + mHomeProcess);
+        pw.println("  mPreviousProcess: " + mPreviousProcess);
         if (mHeavyWeightProcess != null) {
             pw.println("  mHeavyWeightProcess: " + mHeavyWeightProcess);
         }
@@ -8212,6 +8219,7 @@
             pw.print("    BACKUP_APP_ADJ: "); pw.println(ProcessList.BACKUP_APP_ADJ);
             pw.print("    SERVICE_ADJ: "); pw.println(ProcessList.SERVICE_ADJ);
             pw.print("    HOME_APP_ADJ: "); pw.println(ProcessList.HOME_APP_ADJ);
+            pw.print("    PREVIOUS_APP_ADJ: "); pw.println(ProcessList.PREVIOUS_APP_ADJ);
             pw.print("    SERVICE_B_ADJ: "); pw.println(ProcessList.SERVICE_B_ADJ);
             pw.print("    HIDDEN_APP_MIN_ADJ: "); pw.println(ProcessList.HIDDEN_APP_MIN_ADJ);
             pw.print("    HIDDEN_APP_MAX_ADJ: "); pw.println(ProcessList.HIDDEN_APP_MAX_ADJ);
@@ -8228,6 +8236,7 @@
 
         pw.println();
         pw.println("  mHomeProcess: " + mHomeProcess);
+        pw.println("  mPreviousProcess: " + mPreviousProcess);
         if (mHeavyWeightProcess != null) {
             pw.println("  mHeavyWeightProcess: " + mHeavyWeightProcess);
         }
@@ -9009,6 +9018,8 @@
                 oomAdj = buildOomTag("bak", "  ", r.setAdj, ProcessList.HIDDEN_APP_MIN_ADJ);
             } else if (r.setAdj >= ProcessList.SERVICE_B_ADJ) {
                 oomAdj = buildOomTag("svcb ", null, r.setAdj, ProcessList.SERVICE_B_ADJ);
+            } else if (r.setAdj >= ProcessList.PREVIOUS_APP_ADJ) {
+                oomAdj = buildOomTag("prev ", null, r.setAdj, ProcessList.PREVIOUS_APP_ADJ);
             } else if (r.setAdj >= ProcessList.HOME_APP_ADJ) {
                 oomAdj = buildOomTag("home ", null, r.setAdj, ProcessList.HOME_APP_ADJ);
             } else if (r.setAdj >= ProcessList.SERVICE_ADJ) {
@@ -9287,12 +9298,13 @@
             ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ, ProcessList.FOREGROUND_APP_ADJ,
             ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ,
             ProcessList.BACKUP_APP_ADJ, ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ,
-            ProcessList.SERVICE_B_ADJ, ProcessList.HIDDEN_APP_MAX_ADJ
+            ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.HIDDEN_APP_MAX_ADJ
         };
         final String[] oomLabel = new String[] {
                 "System", "Persistent", "Foreground",
                 "Visible", "Perceptible", "Heavy Weight",
-                "Backup", "A Services", "Home", "B Services", "Background"
+                "Backup", "A Services", "Home", "Previous",
+                "B Services", "Background"
         };
         long oomPss[] = new long[oomLabel.length];
         ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])new ArrayList[oomLabel.length];
@@ -9714,7 +9726,10 @@
         if (app == mHomeProcess) {
             mHomeProcess = null;
         }
-        
+        if (app == mPreviousProcess) {
+            mPreviousProcess = null;
+        }
+
         if (restart) {
             // We have components that still need to be running in the
             // process, so re-launch it.
@@ -13112,6 +13127,17 @@
             app.adjType = "home";
         }
 
+        if (adj > ProcessList.PREVIOUS_APP_ADJ && app == mPreviousProcess
+                && app.activities.size() > 0) {
+            // This was the previous process that showed UI to the user.
+            // We want to try to keep it around more aggressively, to give
+            // a good experience around switching between two apps.
+            adj = ProcessList.PREVIOUS_APP_ADJ;
+            schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+            app.hidden = false;
+            app.adjType = "previous";
+        }
+
         if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj
                 + " reason=" + app.adjType);
 
@@ -13841,7 +13867,8 @@
                     } else {
                         numBg++;
                     }
-                } else if (app.curAdj >= ProcessList.HOME_APP_ADJ) {
+                } else if (app.curAdj >= ProcessList.HOME_APP_ADJ
+                        && app.curAdj != ProcessList.SERVICE_B_ADJ) {
                     numBg++;
                 }
             }
@@ -13871,7 +13898,7 @@
                         if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
                             // For these apps we will also finish their activities
                             // to help them free memory.
-                            mMainStack.destroyActivitiesLocked(app, false);
+                            mMainStack.destroyActivitiesLocked(app, false, "trim");
                         }
                     }
                     app.trimMemoryLevel = curLevel;
@@ -13935,7 +13962,7 @@
         }
 
         if (mAlwaysFinishActivities) {
-            mMainStack.destroyActivitiesLocked(null, false);
+            mMainStack.destroyActivitiesLocked(null, false, "always-finish");
         }
     }
 
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 28c3bae..8435eaa 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -947,7 +947,7 @@
         r.state = ActivityState.STOPPED;
         if (!r.finishing) {
             if (r.configDestroy) {
-                destroyActivityLocked(r, true, false);
+                destroyActivityLocked(r, true, false, "stop-config");
                 resumeTopActivityLocked(null);
             }
         }
@@ -976,7 +976,7 @@
                     // instance right now, we need to first completely stop
                     // the current instance before starting the new one.
                     if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
-                    destroyActivityLocked(prev, true, false);
+                    destroyActivityLocked(prev, true, false, "pause-config");
                 } else {
                     mStoppingActivities.add(prev);
                     if (mStoppingActivities.size() > 3) {
@@ -1363,6 +1363,14 @@
                         + ", nowVisible=" + next.nowVisible);
                 }
             }
+
+            if (!prev.finishing && prev.app != null && prev.app != next.app
+                    && prev.app != mService.mHomeProcess) {
+                // We are switching to a new activity that is in a different
+                // process than the previous one.  Note the previous process,
+                // so we can try to keep it around.
+                mService.mPreviousProcess = prev.app;
+            }
         }
 
         // Launching this app's activity, make sure the app is no longer
@@ -3106,7 +3114,7 @@
                 if (DEBUG_STATES) Slog.v(TAG, "Stop failed; moving to STOPPED: " + r);
                 r.state = ActivityState.STOPPED;
                 if (r.configDestroy) {
-                    destroyActivityLocked(r, true, false);
+                    destroyActivityLocked(r, true, false, "stop-except");
                 }
             }
         }
@@ -3281,7 +3289,7 @@
         for (i=0; i<NF; i++) {
             ActivityRecord r = (ActivityRecord)finishes.get(i);
             synchronized (mService) {
-                destroyActivityLocked(r, true, false);
+                destroyActivityLocked(r, true, false, "finish-idle");
             }
         }
 
@@ -3480,7 +3488,7 @@
                 || prevState == ActivityState.INITIALIZING) {
             // If this activity is already stopped, we can just finish
             // it right now.
-            return destroyActivityLocked(r, true, true) ? null : r;
+            return destroyActivityLocked(r, true, true, "finish-imm") ? null : r;
         } else {
             // Need to go through the full pause cycle to get this
             // activity into the stopped state and then finish it.
@@ -3586,7 +3594,7 @@
         }
     }
     
-    final void destroyActivitiesLocked(ProcessRecord owner, boolean oomAdj) {
+    final void destroyActivitiesLocked(ProcessRecord owner, boolean oomAdj, String reason) {
         for (int i=mHistory.size()-1; i>=0; i--) {
             ActivityRecord r = mHistory.get(i);
             if (owner != null && r.app != owner) {
@@ -3597,7 +3605,7 @@
             if (r.app != null && r.haveState && !r.visible && r.stopped && !r.finishing
                     && r.state != ActivityState.DESTROYING
                     && r.state != ActivityState.DESTROYED) {
-                destroyActivityLocked(r, true, oomAdj);
+                destroyActivityLocked(r, true, oomAdj, "trim");
             }
         }
     }
@@ -3609,13 +3617,13 @@
      * but then create a new client-side object for this same HistoryRecord.
      */
     final boolean destroyActivityLocked(ActivityRecord r,
-            boolean removeFromApp, boolean oomAdj) {
+            boolean removeFromApp, boolean oomAdj, String reason) {
         if (DEBUG_SWITCH) Slog.v(
             TAG, "Removing activity: token=" + r
               + ", app=" + (r.app != null ? r.app.processName : "(null)"));
         EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
                 System.identityHashCode(r),
-                r.task.taskId, r.shortComponentName);
+                r.task.taskId, r.shortComponentName, reason);
 
         boolean removedFromHistory = false;
         
@@ -4102,7 +4110,7 @@
             if (r.app == null || r.app.thread == null) {
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                         "Switch is destroying non-running " + r);
-                destroyActivityLocked(r, true, false);
+                destroyActivityLocked(r, true, false, "config");
             } else if (r.state == ActivityState.PAUSING) {
                 // A little annoying: we are waiting for this activity to
                 // finish pausing.  Let's not do anything now, but just
diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags
index aadd37d..a579f44 100644
--- a/services/java/com/android/server/am/EventLogTags.logtags
+++ b/services/java/com/android/server/am/EventLogTags.logtags
@@ -48,7 +48,7 @@
 # Reporting to applications that memory is low
 30017 am_low_memory (Num Processes|1|1)
 # An activity is being destroyed:
-30018 am_destroy_activity (Token|1|5),(Task ID|1|5),(Component Name|3)
+30018 am_destroy_activity (Token|1|5),(Task ID|1|5),(Component Name|3),(Reason|3)
 # An activity has been relaunched, resumed, and is now in the foreground:
 30019 am_relaunch_resume_activity (Token|1|5),(Task ID|1|5),(Component Name|3)
 # An activity has been relaunched:
diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java
index f368a70..af7b314 100644
--- a/services/java/com/android/server/am/ProcessList.java
+++ b/services/java/com/android/server/am/ProcessList.java
@@ -38,11 +38,19 @@
     // This is a process only hosting activities that are not visible,
     // so it can be killed without any disruption.
     static final int HIDDEN_APP_MAX_ADJ = 15;
-    static int HIDDEN_APP_MIN_ADJ = 8;
+    static int HIDDEN_APP_MIN_ADJ = 9;
 
     // The B list of SERVICE_ADJ -- these are the old and decrepit
     // services that aren't as shiny and interesting as the ones in the A list.
-    static final int SERVICE_B_ADJ = 7;
+    static final int SERVICE_B_ADJ = 8;
+
+    // This is the process of the previous application that the user was in.
+    // This process is kept above other things, because it is very common to
+    // switch back to the previous app.  This is important both for recent
+    // task switch (toggling between the two top recent apps) as well as normal
+    // UI flow such as clicking on a URI in the e-mail app to view in the browser,
+    // and then pressing back to return to e-mail.
+    static final int PREVIOUS_APP_ADJ = 7;
 
     // This is a process holding the home application -- we want to try
     // avoiding killing it, even if it would normally be in the background,
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 1107fe9..7bd29d9 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -73,7 +73,7 @@
     private Context mContext;
     private final static String TAG = "Tethering";
     private final static boolean DBG = true;
-    private final static boolean VDBG = false;
+    private final static boolean VDBG = true;
 
     // TODO - remove both of these - should be part of interface inspection/selection stuff
     private String[] mTetherableUsbRegexs;
@@ -920,6 +920,29 @@
                 setTethered(true);
                 sendTetherStateChangedBroadcast();
             }
+
+            void cleanupUpstream() {
+                if (mMyUpstreamIfaceName != null) {
+                    // note that we don't care about errors here.
+                    // sometimes interfaces are gone before we get
+                    // to remove their rules, which generates errors.
+                    // just do the best we can.
+                    try {
+                        // about to tear down NAT; gather remaining statistics
+                        mStatsService.forceUpdate();
+                    } catch (Exception e) {
+                        if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
+                    }
+                    try {
+                        mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
+                    } catch (Exception e) {
+                        if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
+                    }
+                    mMyUpstreamIfaceName = null;
+                }
+                return;
+            }
+
             @Override
             public boolean processMessage(Message message) {
                 if (VDBG) Log.d(TAG, "TetheredState.processMessage what=" + message.what);
@@ -928,23 +951,7 @@
                 switch (message.what) {
                     case CMD_TETHER_UNREQUESTED:
                     case CMD_INTERFACE_DOWN:
-                        if (mMyUpstreamIfaceName != null) {
-                            try {
-                                // about to tear down NAT; gather remaining statistics
-                                mStatsService.forceUpdate();
-
-                                mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
-                                mMyUpstreamIfaceName = null;
-                            } catch (Exception e) {
-                                try {
-                                    mNMService.untetherInterface(mIfaceName);
-                                } catch (Exception ee) {}
-
-                                setLastErrorAndTransitionToInitialState(
-                                        ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR);
-                                break;
-                            }
-                        }
+                        cleanupUpstream();
                         try {
                             mNMService.untetherInterface(mIfaceName);
                         } catch (Exception e) {
@@ -975,23 +982,7 @@
                             if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
                             break;
                         }
-                        if (mMyUpstreamIfaceName != null) {
-                            try {
-                                // about to tear down NAT; gather remaining statistics
-                                mStatsService.forceUpdate();
-
-                                mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
-                                mMyUpstreamIfaceName = null;
-                            } catch (Exception e) {
-                                try {
-                                    mNMService.untetherInterface(mIfaceName);
-                                } catch (Exception ee) {}
-
-                                setLastErrorAndTransitionToInitialState(
-                                        ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR);
-                                break;
-                            }
-                        }
+                        cleanupUpstream();
                         if (newUpstreamIfaceName != null) {
                             try {
                                 mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
@@ -1016,23 +1007,7 @@
                         error = true;
                         // fall through
                     case CMD_TETHER_MODE_DEAD:
-                        if (mMyUpstreamIfaceName != null) {
-                            try {
-                                // about to tear down NAT; gather remaining statistics
-                                mStatsService.forceUpdate();
-
-                                mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
-                                mMyUpstreamIfaceName = null;
-                            } catch (Exception e) {
-                                try {
-                                    mNMService.untetherInterface(mIfaceName);
-                                } catch (Exception ee) {}
-
-                                setLastErrorAndTransitionToInitialState(
-                                        ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR);
-                                break;
-                            }
-                        }
+                        cleanupUpstream();
                         try {
                             mNMService.untetherInterface(mIfaceName);
                         } catch (Exception e) {
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index e610782..289ea1f 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -19,7 +19,6 @@
 import static android.Manifest.permission.ACCESS_NETWORK_STATE;
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.DUMP;
-import static android.Manifest.permission.MANAGE_APP_TOKENS;
 import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.Manifest.permission.READ_PHONE_STATE;
@@ -93,6 +92,7 @@
 import android.os.INetworkManagementService;
 import android.os.IPowerManager;
 import android.os.Message;
+import android.os.MessageQueue.IdleHandler;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.provider.Settings;
@@ -186,8 +186,10 @@
 
     private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
 
-    private static final int MSG_RULES_CHANGED = 0x1;
-    private static final int MSG_METERED_IFACES_CHANGED = 0x2;
+    private static final int MSG_RULES_CHANGED = 1;
+    private static final int MSG_METERED_IFACES_CHANGED = 2;
+    private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 3;
+    private static final int MSG_PROCESS_DIED = 4;
 
     private final Context mContext;
     private final IActivityManager mActivityManager;
@@ -335,37 +337,13 @@
     private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
         @Override
         public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
-            // only someone like AMS should only be calling us
-            mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG);
-
-            synchronized (mRulesLock) {
-                // because a uid can have multiple pids running inside, we need to
-                // remember all pid states and summarize foreground at uid level.
-
-                // record foreground for this specific pid
-                SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
-                if (pidForeground == null) {
-                    pidForeground = new SparseBooleanArray(2);
-                    mUidPidForeground.put(uid, pidForeground);
-                }
-                pidForeground.put(pid, foregroundActivities);
-                computeUidForegroundLocked(uid);
-            }
+            mHandler.obtainMessage(MSG_FOREGROUND_ACTIVITIES_CHANGED,
+                    pid, uid, foregroundActivities).sendToTarget();
         }
 
         @Override
         public void onProcessDied(int pid, int uid) {
-            // only someone like AMS should only be calling us
-            mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG);
-
-            synchronized (mRulesLock) {
-                // clear records and recompute, when they exist
-                final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
-                if (pidForeground != null) {
-                    pidForeground.delete(pid);
-                    computeUidForegroundLocked(uid);
-                }
-            }
+            mHandler.obtainMessage(MSG_PROCESS_DIED, pid, uid).sendToTarget();
         }
     };
 
@@ -1469,6 +1447,40 @@
                     mListeners.finishBroadcast();
                     return true;
                 }
+                case MSG_FOREGROUND_ACTIVITIES_CHANGED: {
+                    final int pid = msg.arg1;
+                    final int uid = msg.arg2;
+                    final boolean foregroundActivities = (Boolean) msg.obj;
+
+                    synchronized (mRulesLock) {
+                        // because a uid can have multiple pids running inside, we need to
+                        // remember all pid states and summarize foreground at uid level.
+
+                        // record foreground for this specific pid
+                        SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+                        if (pidForeground == null) {
+                            pidForeground = new SparseBooleanArray(2);
+                            mUidPidForeground.put(uid, pidForeground);
+                        }
+                        pidForeground.put(pid, foregroundActivities);
+                        computeUidForegroundLocked(uid);
+                    }
+                    return true;
+                }
+                case MSG_PROCESS_DIED: {
+                    final int pid = msg.arg1;
+                    final int uid = msg.arg2;
+
+                    synchronized (mRulesLock) {
+                        // clear records and recompute, when they exist
+                        final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+                        if (pidForeground != null) {
+                            pidForeground.delete(pid);
+                            computeUidForegroundLocked(uid);
+                        }
+                    }
+                    return true;
+                }
                 default: {
                     return false;
                 }
@@ -1571,6 +1583,11 @@
         return intent;
     }
 
+    // @VisibleForTesting
+    public void addIdleHandler(IdleHandler handler) {
+        mHandler.getLooper().getQueue().addIdleHandler(handler);
+    }
+
     private static void collectKeys(SparseIntArray source, SparseBooleanArray target) {
         final int size = source.size();
         for (int i = 0; i < size; i++) {
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 789681e..494c655 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -32,7 +32,6 @@
 import static android.net.NetworkStats.SET_FOREGROUND;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
-import static android.net.NetworkStatsHistory.randomLong;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.NetworkTemplate.buildTemplateWifi;
 import static android.net.TrafficStats.UID_REMOVED;
@@ -49,7 +48,6 @@
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
-import static android.text.format.DateUtils.WEEK_IN_MILLIS;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
 import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
@@ -73,9 +71,11 @@
 import android.net.NetworkInfo;
 import android.net.NetworkState;
 import android.net.NetworkStats;
+import android.net.NetworkStats.NonMonotonicException;
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.os.Binder;
+import android.os.DropBoxManager;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -150,6 +150,12 @@
     /** Sample recent usage after each poll event. */
     private static final boolean ENABLE_SAMPLE_AFTER_POLL = true;
 
+    private static final String TAG_NETSTATS_ERROR = "netstats_error";
+
+    private static final String DEV = "dev";
+    private static final String XT = "xt";
+    private static final String UID = "uid";
+
     private final Context mContext;
     private final INetworkManagementService mNetworkManager;
     private final IAlarmManager mAlarmManager;
@@ -160,6 +166,7 @@
     private final PowerManager.WakeLock mWakeLock;
 
     private IConnectivityManager mConnManager;
+    private DropBoxManager mDropBox;
 
     // @VisibleForTesting
     public static final String ACTION_NETWORK_STATS_POLL =
@@ -306,6 +313,8 @@
 
         // bootstrap initial stats to prevent double-counting later
         bootstrapStats();
+
+        mDropBox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
     }
 
     private void shutdownLocked() {
@@ -621,7 +630,6 @@
             // broadcast.
             final int uid = intent.getIntExtra(EXTRA_UID, 0);
             synchronized (mStatsLock) {
-                // TODO: perform one last stats poll for UID
                 mWakeLock.acquire();
                 try {
                     removeUidLocked(uid);
@@ -829,9 +837,9 @@
 
         // persist when enough network data has occurred
         final long persistNetworkDevDelta = computeStatsDelta(
-                mLastPersistNetworkDevSnapshot, networkDevSnapshot, true).getTotalBytes();
+                mLastPersistNetworkDevSnapshot, networkDevSnapshot, true, DEV).getTotalBytes();
         final long persistNetworkXtDelta = computeStatsDelta(
-                mLastPersistNetworkXtSnapshot, networkXtSnapshot, true).getTotalBytes();
+                mLastPersistNetworkXtSnapshot, networkXtSnapshot, true, XT).getTotalBytes();
         final boolean networkOverThreshold = persistNetworkDevDelta > threshold
                 || persistNetworkXtDelta > threshold;
         if (persistForce || (persistNetwork && networkOverThreshold)) {
@@ -842,8 +850,8 @@
         }
 
         // persist when enough uid data has occurred
-        final long persistUidDelta = computeStatsDelta(mLastPersistUidSnapshot, uidSnapshot, true)
-                .getTotalBytes();
+        final long persistUidDelta = computeStatsDelta(
+                mLastPersistUidSnapshot, uidSnapshot, true, UID).getTotalBytes();
         if (persistForce || (persistUid && persistUidDelta > threshold)) {
             writeUidStatsLocked();
             mLastPersistUidSnapshot = uidSnapshot;
@@ -872,7 +880,7 @@
         final HashSet<String> unknownIface = Sets.newHashSet();
 
         final NetworkStats delta = computeStatsDelta(
-                mLastPollNetworkDevSnapshot, networkDevSnapshot, false);
+                mLastPollNetworkDevSnapshot, networkDevSnapshot, false, DEV);
         final long timeStart = currentTime - delta.getElapsedRealtime();
 
         NetworkStats.Entry entry = null;
@@ -902,7 +910,7 @@
         final HashSet<String> unknownIface = Sets.newHashSet();
 
         final NetworkStats delta = computeStatsDelta(
-                mLastPollNetworkXtSnapshot, networkXtSnapshot, false);
+                mLastPollNetworkXtSnapshot, networkXtSnapshot, false, XT);
         final long timeStart = currentTime - delta.getElapsedRealtime();
 
         NetworkStats.Entry entry = null;
@@ -931,9 +939,10 @@
     private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) {
         ensureUidStatsLoadedLocked();
 
-        final NetworkStats delta = computeStatsDelta(mLastPollUidSnapshot, uidSnapshot, false);
+        final NetworkStats delta = computeStatsDelta(
+                mLastPollUidSnapshot, uidSnapshot, false, UID);
         final NetworkStats operationsDelta = computeStatsDelta(
-                mLastPollOperationsSnapshot, mOperations, false);
+                mLastPollOperationsSnapshot, mOperations, false, UID);
         final long timeStart = currentTime - delta.getElapsedRealtime();
 
         NetworkStats.Entry entry = null;
@@ -1014,6 +1023,9 @@
     private void removeUidLocked(int uid) {
         ensureUidStatsLoadedLocked();
 
+        // perform one last poll before removing
+        performPollLocked(FLAG_PERSIST_ALL);
+
         final ArrayList<UidStatsKey> knownKeys = Lists.newArrayList();
         knownKeys.addAll(mUidStats.keySet());
 
@@ -1031,6 +1043,10 @@
             }
         }
 
+        // clear UID from current stats snapshot
+        mLastPollUidSnapshot = mLastPollUidSnapshot.withoutUid(uid);
+        mLastPollNetworkXtSnapshot = computeNetworkXtSnapshotFromUid(mLastPollUidSnapshot);
+
         // clear kernel stats associated with UID
         resetKernelUidStats(uid);
 
@@ -1490,10 +1506,25 @@
      * Return the delta between two {@link NetworkStats} snapshots, where {@code
      * before} can be {@code null}.
      */
-    private static NetworkStats computeStatsDelta(
-            NetworkStats before, NetworkStats current, boolean collectStale) {
+    private NetworkStats computeStatsDelta(
+            NetworkStats before, NetworkStats current, boolean collectStale, String type) {
         if (before != null) {
-            return current.subtractClamped(before);
+            try {
+                return current.subtract(before);
+            } catch (NonMonotonicException e) {
+                Log.w(TAG, "found non-monotonic values; saving to dropbox");
+
+                // record error for debugging
+                final StringBuilder builder = new StringBuilder();
+                builder.append("found non-monotonic " + type + "values at left[" + e.leftIndex
+                        + "] - right[" + e.rightIndex + "]\n");
+                builder.append("left=").append(e.left).append('\n');
+                builder.append("right=").append(e.right).append('\n');
+                mDropBox.addText(TAG_NETSTATS_ERROR, builder.toString());
+
+                // return empty delta to avoid recording broken stats
+                return new NetworkStats(0L, 10);
+            }
         } else if (collectStale) {
             // caller is okay collecting stale stats for first call.
             return current;
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index e892b5e..368595f 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -66,6 +66,7 @@
 import android.os.Binder;
 import android.os.INetworkManagementService;
 import android.os.IPowerManager;
+import android.os.MessageQueue.IdleHandler;
 import android.test.AndroidTestCase;
 import android.test.mock.MockPackageManager;
 import android.test.suitebuilder.annotation.LargeTest;
@@ -87,6 +88,7 @@
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.logging.Handler;
 
 import libcore.io.IoUtils;
 
@@ -100,6 +102,10 @@
     private static final long TEST_START = 1194220800000L;
     private static final String TEST_IFACE = "test0";
 
+    private static final long KB_IN_BYTES = 1024;
+    private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
+    private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
+
     private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi();
 
     private BroadcastInterceptingContext mServiceContext;
@@ -255,31 +261,37 @@
         mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
         mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
         mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, false);
+        waitUntilIdle();
         assertFalse(mService.isUidForeground(UID_A));
         assertFalse(mService.isUidForeground(UID_B));
 
         // push one of the shared pids into foreground
         mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
+        waitUntilIdle();
         assertTrue(mService.isUidForeground(UID_A));
         assertFalse(mService.isUidForeground(UID_B));
 
         // and swap another uid into foreground
         mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
         mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, true);
+        waitUntilIdle();
         assertFalse(mService.isUidForeground(UID_A));
         assertTrue(mService.isUidForeground(UID_B));
 
         // push both pid into foreground
         mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
         mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
+        waitUntilIdle();
         assertTrue(mService.isUidForeground(UID_A));
 
         // pull one out, should still be foreground
         mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+        waitUntilIdle();
         assertTrue(mService.isUidForeground(UID_A));
 
         // pull final pid out, should now be background
         mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
+        waitUntilIdle();
         assertFalse(mService.isUidForeground(UID_A));
     }
 
@@ -528,13 +540,14 @@
 
         // TODO: consider making strongly ordered mock
         expectRemoveInterfaceQuota(TEST_IFACE);
-        expectSetInterfaceQuota(TEST_IFACE, 1536L);
+        expectSetInterfaceQuota(TEST_IFACE, (2 * MB_IN_BYTES) - 512);
 
         expectClearNotifications();
         future = expectMeteredIfacesChanged(TEST_IFACE);
 
         replay();
-        setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L, SNOOZE_NEVER));
+        setNetworkPolicies(new NetworkPolicy(
+                sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, SNOOZE_NEVER));
         future.get();
         verifyAndReset();
     }
@@ -590,8 +603,8 @@
             future = expectMeteredIfacesChanged();
 
             replay();
-            setNetworkPolicies(
-                    new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L, SNOOZE_NEVER));
+            setNetworkPolicies(new NetworkPolicy(
+                    sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, SNOOZE_NEVER));
             future.get();
             verifyAndReset();
         }
@@ -609,7 +622,7 @@
                     .andReturn(stats).atLeastOnce();
 
             expectRemoveInterfaceQuota(TEST_IFACE);
-            expectSetInterfaceQuota(TEST_IFACE, 2048L);
+            expectSetInterfaceQuota(TEST_IFACE, 2 * MB_IN_BYTES);
 
             expectClearNotifications();
             future = expectMeteredIfacesChanged(TEST_IFACE);
@@ -623,7 +636,7 @@
         // go over warning, which should kick notification
         incrementCurrentTime(MINUTE_IN_MILLIS);
         stats = new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 1536L, 15L, 0L, 0L);
+                .addIfaceValues(TEST_IFACE, 1536 * KB_IN_BYTES, 15L, 0L, 0L);
 
         {
             expectCurrentTime();
@@ -643,7 +656,7 @@
         // go over limit, which should kick notification and dialog
         incrementCurrentTime(MINUTE_IN_MILLIS);
         stats = new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 5120L, 512L, 0L, 0L);
+                .addIfaceValues(TEST_IFACE, 5 * MB_IN_BYTES, 512L, 0L, 0L);
 
         {
             expectCurrentTime();
@@ -799,6 +812,32 @@
         }
     }
 
+    private static class IdleFuture extends AbstractFuture<Void> implements IdleHandler {
+        @Override
+        public Void get() throws InterruptedException, ExecutionException {
+            try {
+                return get(5, TimeUnit.SECONDS);
+            } catch (TimeoutException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        /** {@inheritDoc} */
+        public boolean queueIdle() {
+            set(null);
+            return false;
+        }
+    }
+
+    /**
+     * Wait until {@link #mService} internal {@link Handler} is idle.
+     */
+    private void waitUntilIdle() throws Exception {
+        final IdleFuture future = new IdleFuture();
+        mService.addIdleHandler(future);
+        future.get();
+    }
+
     private static void assertTimeEquals(long expected, long actual) {
         if (expected != actual) {
             fail("expected " + formatTime(expected) + " but was actually " + formatTime(actual));
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index f7dff23..fbc171b 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -83,6 +83,7 @@
     private static final String TAG = "NetworkStatsServiceTest";
 
     private static final String TEST_IFACE = "test0";
+    private static final String TEST_IFACE2 = "test1";
     private static final long TEST_START = 1194220800000L;
 
     private static final String IMSI_1 = "310004";
@@ -418,8 +419,12 @@
         expectCurrentTime();
         expectDefaultSettings();
         expectNetworkState(buildMobile3gState(IMSI_2));
-        expectNetworkStatsSummary(buildEmptyStats());
-        expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
+                .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
+        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
         expectNetworkStatsPoll();
 
         replay();
@@ -432,9 +437,11 @@
         expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 128L, 1L, 1024L, 8L));
+                .addIfaceValues(TEST_IFACE, 2176L, 17L, 1536L, 12L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 1024L, 8L, 0L)
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
         expectNetworkStatsPoll();
 
@@ -499,6 +506,15 @@
         // special "removed" bucket.
         expectCurrentTime();
         expectDefaultSettings();
+        expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
+                .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
+        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
+                .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
+                .addValues(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+        expectNetworkStatsPoll();
+
         replay();
         final Intent intent = new Intent(ACTION_UID_REMOVED);
         intent.putExtra(EXTRA_UID, UID_BLUE);
@@ -553,9 +569,11 @@
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectCurrentTime();
         expectDefaultSettings();
-        expectNetworkState(buildMobile4gState());
+        expectNetworkState(buildMobile4gState(TEST_IFACE2));
         expectNetworkStatsSummary(buildEmptyStats());
-        expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
         expectNetworkStatsPoll();
 
         replay();
@@ -569,8 +587,10 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
-                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .addValues(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+                .addValues(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
         expectNetworkStatsPoll();
 
         mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
@@ -625,6 +645,8 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
+                .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0L));
         expectNetworkStatsPoll();
 
@@ -881,11 +903,11 @@
         return new NetworkState(info, prop, null, subscriberId);
     }
 
-    private static NetworkState buildMobile4gState() {
+    private static NetworkState buildMobile4gState(String iface) {
         final NetworkInfo info = new NetworkInfo(TYPE_WIMAX, 0, null, null);
         info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
-        prop.setInterfaceName(TEST_IFACE);
+        prop.setInterfaceName(iface);
         return new NetworkState(info, prop, null);
     }
 
diff --git a/tests/FrameworkPerf/res/layout/button_layout.xml b/tests/FrameworkPerf/res/layout/button_layout.xml
new file mode 100644
index 0000000..7786a25
--- /dev/null
+++ b/tests/FrameworkPerf/res/layout/button_layout.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+    <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:text="FooBarYou" />
+</LinearLayout>
diff --git a/tests/FrameworkPerf/res/layout/image_button_layout.xml b/tests/FrameworkPerf/res/layout/image_button_layout.xml
new file mode 100644
index 0000000..65b12b3
--- /dev/null
+++ b/tests/FrameworkPerf/res/layout/image_button_layout.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+    <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:src="@drawable/stat_happy"/>
+</LinearLayout>
diff --git a/tests/FrameworkPerf/res/layout/large_layout.xml b/tests/FrameworkPerf/res/layout/large_layout.xml
index b6ac88c..39bbe34 100644
--- a/tests/FrameworkPerf/res/layout/large_layout.xml
+++ b/tests/FrameworkPerf/res/layout/large_layout.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
+<!-- Copyright (C) 2011 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
diff --git a/tests/FrameworkPerf/res/layout/small_layout.xml b/tests/FrameworkPerf/res/layout/small_layout.xml
index 9fcbb26..e78a176 100644
--- a/tests/FrameworkPerf/res/layout/small_layout.xml
+++ b/tests/FrameworkPerf/res/layout/small_layout.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!-- Copyright (C) 2011 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
diff --git a/tests/FrameworkPerf/res/layout/view_layout.xml b/tests/FrameworkPerf/res/layout/view_layout.xml
new file mode 100644
index 0000000..0171eef
--- /dev/null
+++ b/tests/FrameworkPerf/res/layout/view_layout.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+    <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java
index 5e15224..3979902 100644
--- a/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java
+++ b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java
@@ -20,6 +20,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.os.Bundle;
@@ -29,13 +31,16 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.SystemClock;
+import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.Xml;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
+import android.widget.Button;
 import android.widget.Spinner;
 import android.widget.TextView;
 
@@ -46,6 +51,9 @@
 import java.io.RandomAccessFile;
 import java.util.ArrayList;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 /**
  * So you thought sync used up your battery life.
  */
@@ -57,8 +65,10 @@
 
     Spinner mFgSpinner;
     Spinner mBgSpinner;
-    TextView mLog;
     TextView mTestTime;
+    Button mStartButton;
+    Button mStopButton;
+    TextView mLog;
     PowerManager.WakeLock mPartialWakeLock;
 
     long mMaxRunTime = 5000;
@@ -100,12 +110,21 @@
             new WriteFileOp(), new ReadFileOp(),
             new ReadFileOp(), new WriteFileOp(),
             new ReadFileOp(), new ReadFileOp(),
+            new OpenXmlResOp(), new NoOp(),
+            new ReadXmlAttrsOp(), new NoOp(),
             new ParseXmlResOp(), new NoOp(),
             new ParseLargeXmlResOp(), new NoOp(),
             new LayoutInflaterOp(), new NoOp(),
             new LayoutInflaterLargeOp(), new NoOp(),
+            new LayoutInflaterViewOp(), new NoOp(),
+            new LayoutInflaterButtonOp(), new NoOp(),
+            new LayoutInflaterImageButtonOp(), new NoOp(),
+            new CreateBitmapOp(), new NoOp(),
+            new CreateRecycleBitmapOp(), new NoOp(),
             new LoadSmallBitmapOp(), new NoOp(),
+            new LoadRecycleSmallBitmapOp(), new NoOp(),
             new LoadLargeBitmapOp(), new NoOp(),
+            new LoadRecycleLargeBitmapOp(), new NoOp(),
             new LoadSmallScaledBitmapOp(), new NoOp(),
             new LoadLargeScaledBitmapOp(), new NoOp(),
     };
@@ -122,12 +141,21 @@
             new CreateWriteSyncFileOp(),
             new WriteFileOp(),
             new ReadFileOp(),
+            new OpenXmlResOp(),
+            new ReadXmlAttrsOp(),
             new ParseXmlResOp(),
             new ParseLargeXmlResOp(),
             new LayoutInflaterOp(),
             new LayoutInflaterLargeOp(),
+            new LayoutInflaterViewOp(),
+            new LayoutInflaterButtonOp(),
+            new LayoutInflaterImageButtonOp(),
+            new CreateBitmapOp(),
+            new CreateRecycleBitmapOp(),
             new LoadSmallBitmapOp(),
+            new LoadRecycleSmallBitmapOp(),
             new LoadLargeBitmapOp(),
+            new LoadRecycleLargeBitmapOp(),
             new LoadSmallScaledBitmapOp(),
             new LoadLargeScaledBitmapOp(),
     };
@@ -208,17 +236,22 @@
         mBgSpinner.setAdapter(adapter);
         mBgSpinner.setOnItemSelectedListener(this);
 
-        findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
+        mTestTime = (TextView)findViewById(R.id.testtime);
+
+        mStartButton = (Button)findViewById(R.id.start);
+        mStartButton.setOnClickListener(new View.OnClickListener() {
             @Override public void onClick(View v) {
                 startRunning();
             }
         });
-        findViewById(R.id.stop).setOnClickListener(new View.OnClickListener() {
+        mStopButton = (Button)findViewById(R.id.stop);
+        mStopButton.setOnClickListener(new View.OnClickListener() {
             @Override public void onClick(View v) {
                 stopRunning();
             }
         });
-        mTestTime = (TextView)findViewById(R.id.testtime);
+        mStopButton.setEnabled(false);
+
         mLog = (TextView)findViewById(R.id.log);
 
         PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
@@ -267,9 +300,17 @@
             fgOp = mFgTest;
             bgOp = mBgTest;
         } else if (mFgTest != null) {
+            // Skip null test.
+            if (mCurOpIndex == 0) {
+                mCurOpIndex = 1;
+            }
             fgOp = mFgTest;
             bgOp = mAvailOps[mCurOpIndex];
         } else {
+            // Skip null test.
+            if (mCurOpIndex == 0) {
+                mCurOpIndex = 1;
+            }
             fgOp = mAvailOps[mCurOpIndex];
             bgOp = mBgTest;
         }
@@ -297,12 +338,13 @@
                         stopRunning();
                         return;
                     }
-                }
-                mCurOpIndex++;
-                if (mCurOpIndex >= mAvailOps.length) {
-                    log("Finished");
-                    stopRunning();
-                    return;
+                } else {
+                    mCurOpIndex++;
+                    if (mCurOpIndex >= mAvailOps.length) {
+                        log("Finished");
+                        stopRunning();
+                        return;
+                    }
                 }
                 startCurOp();
             }
@@ -313,6 +355,11 @@
         if (!mStarted) {
             log("Start");
             mStarted = true;
+            mStartButton.setEnabled(false);
+            mStopButton.setEnabled(true);
+            mTestTime.setEnabled(false);
+            mFgSpinner.setEnabled(false);
+            mBgSpinner.setEnabled(false);
             updateWakeLock();
             startService(new Intent(this, SchedulerService.class));
             mCurOpIndex = 0;
@@ -325,6 +372,11 @@
     void stopRunning() {
         if (mStarted) {
             mStarted = false;
+            mStartButton.setEnabled(true);
+            mStopButton.setEnabled(false);
+            mTestTime.setEnabled(true);
+            mFgSpinner.setEnabled(true);
+            mBgSpinner.setEnabled(true);
             updateWakeLock();
             stopService(new Intent(this, SchedulerService.class));
             for (int i=0; i<mResults.size(); i++) {
@@ -333,7 +385,7 @@
                 float bgMsPerOp = result.getBgMsPerOp();
                 String fgMsPerOpStr = fgMsPerOp != 0 ? Float.toString(fgMsPerOp) : "";
                 String bgMsPerOpStr = bgMsPerOp != 0 ? Float.toString(bgMsPerOp) : "";
-                Log.i(TAG, "\t" + result.name + "\t" + result.fgOps
+                Log.i("PerfRes", "\t" + result.name + "\t" + result.fgOps
                         + "\t" + result.getFgMsPerOp() + "\t" + result.fgTime
                         + "\t" + result.fgLongName + "\t" + result.bgOps
                         + "\t" + result.getBgMsPerOp() + "\t" + result.bgTime
@@ -662,6 +714,71 @@
         }
     }
 
+    static class OpenXmlResOp extends Op {
+        Context mContext;
+
+        OpenXmlResOp() {
+            super("OpenXmlRes", "Open (and close) an XML resource");
+        }
+
+        void onInit(Context context, boolean foreground) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            XmlResourceParser parser = mContext.getResources().getLayout(R.xml.simple);
+            parser.close();
+            return true;
+        }
+    }
+
+    static class ReadXmlAttrsOp extends Op {
+        Context mContext;
+        XmlResourceParser mParser;
+        AttributeSet mAttrs;
+
+        ReadXmlAttrsOp() {
+            super("ReadXmlAttrs", "Read attributes from an XML tag");
+        }
+
+        void onInit(Context context, boolean foreground) {
+            mContext = context;
+            mParser = mContext.getResources().getLayout(R.xml.simple);
+            mAttrs = Xml.asAttributeSet(mParser);
+
+            int eventType;
+            try {
+                // Find the first <item> tag.
+                eventType = mParser.getEventType();
+                String tagName;
+                do {
+                    if (eventType == XmlPullParser.START_TAG) {
+                        tagName = mParser.getName();
+                        if (tagName.equals("item")) {
+                            break;
+                        }
+                    }
+                    eventType = mParser.next();
+                } while (eventType != XmlPullParser.END_DOCUMENT);
+            } catch (XmlPullParserException e) {
+                throw new RuntimeException("I died", e);
+            } catch (IOException e) {
+                throw new RuntimeException("I died", e);
+            }
+        }
+
+        void onTerm(Context context) {
+            mParser.close();
+        }
+
+        boolean onRun() {
+            TypedArray a = mContext.obtainStyledAttributes(mAttrs,
+                    com.android.internal.R.styleable.MenuItem);
+            a.recycle();
+            return true;
+        }
+    }
+
     static class ParseXmlResOp extends Op {
         Context mContext;
 
@@ -702,7 +819,7 @@
         Context mContext;
 
         LayoutInflaterOp() {
-            super("LayoutInflaterOp", "Inflate layout resource");
+            super("LayoutInflater", "Inflate layout resource");
         }
 
         void onInit(Context context, boolean foreground) {
@@ -724,7 +841,7 @@
         Context mContext;
 
         LayoutInflaterLargeOp() {
-            super("LayoutInflaterLargeOp", "Inflate large layout resource");
+            super("LayoutInflaterLarge", "Inflate large layout resource");
         }
 
         void onInit(Context context, boolean foreground) {
@@ -742,6 +859,111 @@
         }
     }
 
+    static class LayoutInflaterViewOp extends Op {
+        Context mContext;
+
+        LayoutInflaterViewOp() {
+            super("LayoutInflaterView", "Inflate layout with 50 View objects");
+        }
+
+        void onInit(Context context, boolean foreground) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            if (Looper.myLooper() == null) {
+                Looper.prepare();
+            }
+            LayoutInflater inf = (LayoutInflater)mContext.getSystemService(
+                    Context.LAYOUT_INFLATER_SERVICE);
+            inf.inflate(R.layout.view_layout, null);
+            return true;
+        }
+    }
+
+    static class LayoutInflaterButtonOp extends Op {
+        Context mContext;
+
+        LayoutInflaterButtonOp() {
+            super("LayoutInflaterButton", "Inflate layout with 50 Button objects");
+        }
+
+        void onInit(Context context, boolean foreground) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            if (Looper.myLooper() == null) {
+                Looper.prepare();
+            }
+            LayoutInflater inf = (LayoutInflater)mContext.getSystemService(
+                    Context.LAYOUT_INFLATER_SERVICE);
+            inf.inflate(R.layout.button_layout, null);
+            return true;
+        }
+    }
+
+    static class LayoutInflaterImageButtonOp extends Op {
+        Context mContext;
+
+        LayoutInflaterImageButtonOp() {
+            super("LayoutInflaterImageButton", "Inflate layout with 50 ImageButton objects");
+        }
+
+        void onInit(Context context, boolean foreground) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            if (Looper.myLooper() == null) {
+                Looper.prepare();
+            }
+            LayoutInflater inf = (LayoutInflater)mContext.getSystemService(
+                    Context.LAYOUT_INFLATER_SERVICE);
+            inf.inflate(R.layout.image_button_layout, null);
+            return true;
+        }
+    }
+
+    static class CreateBitmapOp extends Op {
+        Context mContext;
+
+        CreateBitmapOp() {
+            super("CreateBitmap", "Create a Bitmap");
+        }
+
+        void onInit(Context context, boolean foreground) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            BitmapFactory.Options opts = new BitmapFactory.Options();
+            opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
+            Bitmap bm = Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888);
+            return true;
+        }
+    }
+
+    static class CreateRecycleBitmapOp extends Op {
+        Context mContext;
+
+        CreateRecycleBitmapOp() {
+            super("CreateRecycleBitmap", "Create and recycle a Bitmap");
+        }
+
+        void onInit(Context context, boolean foreground) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            BitmapFactory.Options opts = new BitmapFactory.Options();
+            opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
+            Bitmap bm = Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888);
+            bm.recycle();
+            return true;
+        }
+    }
+
     static class LoadSmallBitmapOp extends Op {
         Context mContext;
 
@@ -758,6 +980,26 @@
             opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
             Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
                     R.drawable.stat_sample, opts);
+            return true;
+        }
+    }
+
+    static class LoadRecycleSmallBitmapOp extends Op {
+        Context mContext;
+
+        LoadRecycleSmallBitmapOp() {
+            super("LoadRecycleSmallBitmap", "Load and recycle small raw bitmap");
+        }
+
+        void onInit(Context context, boolean foreground) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            BitmapFactory.Options opts = new BitmapFactory.Options();
+            opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
+            Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
+                    R.drawable.stat_sample, opts);
             bm.recycle();
             return true;
         }
@@ -779,6 +1021,26 @@
             opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
             Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
                     R.drawable.wallpaper_goldengate, opts);
+            return true;
+        }
+    }
+
+    static class LoadRecycleLargeBitmapOp extends Op {
+        Context mContext;
+
+        LoadRecycleLargeBitmapOp() {
+            super("LoadRecycleLargeBitmap", "Load and recycle large raw bitmap");
+        }
+
+        void onInit(Context context, boolean foreground) {
+            mContext = context;
+        }
+
+        boolean onRun() {
+            BitmapFactory.Options opts = new BitmapFactory.Options();
+            opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
+            Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
+                    R.drawable.wallpaper_goldengate, opts);
             bm.recycle();
             return true;
         }
@@ -800,7 +1062,6 @@
             opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
             Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
                     R.drawable.stat_sample_scale, opts);
-            bm.recycle();
             return true;
         }
     }
@@ -821,7 +1082,6 @@
             opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
             Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
                     R.drawable.wallpaper_goldengate_scale, opts);
-            bm.recycle();
             return true;
         }
     }
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index e981da7..a46771b 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1651,6 +1651,7 @@
             mDhcpInfoInternal = dhcpInfoInternal;
         }
         mLastSignalLevel = -1; // force update of signal strength
+        mReconnectCount = 0; //Reset IP failure tracking
         WifiConfigStore.setIpConfiguration(mLastNetworkId, dhcpInfoInternal);
         InetAddress addr = NetworkUtils.numericToInetAddress(dhcpInfoInternal.ipAddress);
         mWifiInfo.setInetAddress(addr);
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 1b02774..6bb22a4 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -81,7 +81,7 @@
  */
 public class WifiP2pService extends IWifiP2pManager.Stub {
     private static final String TAG = "WifiP2pService";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
     private static final String NETWORKTYPE = "WIFI_P2P";
 
     private Context mContext;
@@ -131,12 +131,22 @@
     /* User rejected to disable Wi-Fi in order to enable p2p */
     private static final int WIFI_DISABLE_USER_REJECT       =   BASE + 5;
 
+    /* User accepted a group negotiation request */
+    private static final int GROUP_NEGOTIATION_USER_ACCEPT  =   BASE + 6;
+    /* User rejected a group negotiation request */
+    private static final int GROUP_NEGOTIATION_USER_REJECT  =   BASE + 7;
+
+    /* User accepted a group invitation request */
+    private static final int GROUP_INVITATION_USER_ACCEPT   =   BASE + 8;
+    /* User rejected a group invitation request */
+    private static final int GROUP_INVITATION_USER_REJECT   =   BASE + 9;
+
     /* Airplane mode changed */
-    private static final int AIRPLANE_MODE_CHANGED          =   BASE + 6;
+    private static final int AIRPLANE_MODE_CHANGED          =   BASE + 10;
     /* Emergency callback mode */
-    private static final int EMERGENCY_CALLBACK_MODE        =   BASE + 7;
-    private static final int WPS_PBC                        =   BASE + 8;
-    private static final int WPS_PIN                        =   BASE + 9;
+    private static final int EMERGENCY_CALLBACK_MODE        =   BASE + 11;
+    private static final int WPS_PBC                        =   BASE + 12;
+    private static final int WPS_PIN                        =   BASE + 13;
 
     private final boolean mP2pSupported;
 
@@ -260,6 +270,10 @@
         private P2pEnabledState mP2pEnabledState = new P2pEnabledState();
         // Inactive is when p2p is enabled with no connectivity
         private InactiveState mInactiveState = new InactiveState();
+        private UserAuthorizingGroupNegotiationState mUserAuthorizingGroupNegotiationState
+                = new UserAuthorizingGroupNegotiationState();
+        private UserAuthorizingGroupInvitationState mUserAuthorizingGroupInvitationState
+                = new UserAuthorizingGroupInvitationState();
         private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState();
         private GroupCreatedState mGroupCreatedState = new GroupCreatedState();
 
@@ -290,6 +304,8 @@
                 addState(mP2pEnablingState, mDefaultState);
                 addState(mP2pEnabledState, mDefaultState);
                     addState(mInactiveState, mP2pEnabledState);
+                        addState(mUserAuthorizingGroupNegotiationState, mInactiveState);
+                        addState(mUserAuthorizingGroupInvitationState, mInactiveState);
                     addState(mGroupNegotiationState, mP2pEnabledState);
                     addState(mGroupCreatedState, mP2pEnabledState);
 
@@ -379,6 +395,10 @@
                     // Ignore
                 case WIFI_DISABLE_USER_ACCEPT:
                 case WIFI_DISABLE_USER_REJECT:
+                case GROUP_NEGOTIATION_USER_ACCEPT:
+                case GROUP_NEGOTIATION_USER_REJECT:
+                case GROUP_INVITATION_USER_ACCEPT:
+                case GROUP_INVITATION_USER_REJECT:
                 case GROUP_NEGOTIATION_TIMED_OUT:
                     break;
                 default:
@@ -747,6 +767,7 @@
                 case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
                     mSavedGoNegotiationConfig = (WifiP2pConfig) message.obj;
                     notifyP2pGoNegotationRequest(mSavedGoNegotiationConfig);
+                    transitionTo(mUserAuthorizingGroupNegotiationState);
                     break;
                 case WifiP2pManager.CREATE_GROUP:
                     mPersistGroup = true;
@@ -761,6 +782,7 @@
                 case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
                     WifiP2pGroup group = (WifiP2pGroup) message.obj;
                     notifyP2pInvitationReceived(group);
+                    transitionTo(mUserAuthorizingGroupInvitationState);
                     break;
                 default:
                     return NOT_HANDLED;
@@ -769,6 +791,70 @@
         }
     }
 
+    class UserAuthorizingGroupNegotiationState extends State {
+        @Override
+        public void enter() {
+            if (DBG) logd(getName());
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) logd(getName() + message.toString());
+            switch (message.what) {
+                case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
+                case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
+                    //Ignore additional connection requests
+                    break;
+                case GROUP_NEGOTIATION_USER_ACCEPT:
+                    sendMessage(WifiP2pManager.CONNECT, mSavedGoNegotiationConfig);
+                    mSavedGoNegotiationConfig = null;
+                    break;
+                case GROUP_NEGOTIATION_USER_REJECT:
+                    if (DBG) logd("User rejected incoming negotiation request");
+                    mSavedGoNegotiationConfig = null;
+                    transitionTo(mInactiveState);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+    }
+
+    class UserAuthorizingGroupInvitationState extends State {
+        @Override
+        public void enter() {
+            if (DBG) logd(getName());
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) logd(getName() + message.toString());
+            switch (message.what) {
+                case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
+                case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
+                    //Ignore additional connection requests
+                    break;
+                case GROUP_INVITATION_USER_ACCEPT:
+                    if (DBG) logd(getName() + " connect to invited group");
+                    WifiP2pConfig config = new WifiP2pConfig();
+                    config.deviceAddress = mSavedP2pGroup.getOwner().deviceAddress;
+                    sendMessage(WifiP2pManager.CONNECT, config);
+                    mSavedP2pGroup = null;
+                    break;
+                case GROUP_INVITATION_USER_REJECT:
+                    if (DBG) logd("User rejected incoming invitation request");
+                    mSavedP2pGroup = null;
+                    transitionTo(mInactiveState);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+    }
+
+
     class GroupNegotiationState extends State {
         @Override
         public void enter() {
@@ -1091,15 +1177,14 @@
                                 mSavedGoNegotiationConfig.wps.setup = WpsInfo.KEYPAD;
                                 mSavedGoNegotiationConfig.wps.pin = pin.getText().toString();
                             }
-                            sendMessage(WifiP2pManager.CONNECT, mSavedGoNegotiationConfig);
-                            mSavedGoNegotiationConfig = null;
+                            sendMessage(GROUP_NEGOTIATION_USER_ACCEPT);
                         }
                     })
             .setNegativeButton(r.getString(R.string.cancel), new OnClickListener() {
                         @Override
                         public void onClick(DialogInterface dialog, int which) {
                             if (DBG) logd(getName() + " ignore connect");
-                            mSavedGoNegotiationConfig = null;
+                            sendMessage(GROUP_NEGOTIATION_USER_REJECT);
                         }
                     })
             .create();
@@ -1180,14 +1265,16 @@
             .setView(textEntryView)
             .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
                         public void onClick(DialogInterface dialog, int which) {
-                                WifiP2pConfig config = new WifiP2pConfig();
-                                config.deviceAddress = mSavedP2pGroup.getOwner().deviceAddress;
-                                if (DBG) logd(getName() + " connect to invited group");
-                                sendMessage(WifiP2pManager.CONNECT, config);
-                                mSavedP2pGroup = null;
+                            sendMessage(GROUP_INVITATION_USER_ACCEPT);
                         }
                     })
-            .setNegativeButton(r.getString(R.string.cancel), null)
+            .setNegativeButton(r.getString(R.string.cancel), new OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            if (DBG) logd(getName() + " ignore invite");
+                            sendMessage(GROUP_INVITATION_USER_REJECT);
+                        }
+                    })
             .create();
 
         pin.setVisibility(View.GONE);