Merge "Toggle wallpaper window flag when setting custom keyguard background" into klp-dev
diff --git a/api/current.txt b/api/current.txt
index a8b5efd..dc21e90 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10693,10 +10693,6 @@
     method public int getMinFrequency();
   }
 
-  public abstract interface FlushCompleteListener {
-    method public abstract void onFlushCompleted(android.hardware.Sensor);
-  }
-
   public class GeomagneticField {
     ctor public GeomagneticField(float, float, float, long);
     method public float getDeclination();
@@ -10754,6 +10750,10 @@
     method public abstract void onSensorChanged(android.hardware.SensorEvent);
   }
 
+  public abstract interface SensorEventListener2 implements android.hardware.SensorEventListener {
+    method public abstract void onFlushCompleted(android.hardware.Sensor);
+  }
+
   public abstract deprecated interface SensorListener {
     method public abstract void onAccuracyChanged(int, int);
     method public abstract void onSensorChanged(int, float[]);
@@ -10761,7 +10761,7 @@
 
   public abstract class SensorManager {
     method public boolean cancelTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
-    method public boolean flush(android.hardware.Sensor);
+    method public boolean flush(android.hardware.SensorEventListener);
     method public static float getAltitude(float, float);
     method public static void getAngleChange(float[], float[], float[]);
     method public android.hardware.Sensor getDefaultSensor(int);
@@ -10775,9 +10775,9 @@
     method public deprecated boolean registerListener(android.hardware.SensorListener, int);
     method public deprecated boolean registerListener(android.hardware.SensorListener, int, int);
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
-    method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, int, android.hardware.FlushCompleteListener);
+    method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int);
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, android.os.Handler);
-    method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, int, android.os.Handler, android.hardware.FlushCompleteListener);
+    method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler);
     method public static boolean remapCoordinateSystem(float[], int, int, float[]);
     method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
     method public deprecated void unregisterListener(android.hardware.SensorListener);
diff --git a/core/java/android/hardware/FlushCompleteListener.java b/core/java/android/hardware/SensorEventListener2.java
similarity index 89%
rename from core/java/android/hardware/FlushCompleteListener.java
rename to core/java/android/hardware/SensorEventListener2.java
index fbdf4c8..4c3b429 100644
--- a/core/java/android/hardware/FlushCompleteListener.java
+++ b/core/java/android/hardware/SensorEventListener2.java
@@ -19,7 +19,7 @@
 /**
  * Used for receiving a notification when a flush() has been successfully completed.
  */
-public interface FlushCompleteListener {
+public interface SensorEventListener2 extends SensorEventListener {
     /**
      * Called after flush() is completed. This flush() could have been initiated by this application
      * or some other application. All the events in the batch at the point when the flush was called
@@ -28,7 +28,7 @@
      *
      * @param sensor The {@link android.hardware.Sensor Sensor} on which flush was called.
      *
-     * @see android.hardware.SensorManager#flush(Sensor)
+     * @see android.hardware.SensorManager#flush(SensorEventListener)
      */
     public void onFlushCompleted(Sensor sensor);
 }
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 8a4aa1d..b931313 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -533,8 +533,6 @@
      *
      * @see #unregisterListener(SensorEventListener)
      * @see #registerListener(SensorEventListener, Sensor, int)
-     *
-     * @throws IllegalArgumentException when sensor is a trigger sensor.
      */
     public void unregisterListener(SensorEventListener listener, Sensor sensor) {
         if (listener == null || sensor == null) {
@@ -601,7 +599,6 @@
      * @see #unregisterListener(SensorEventListener)
      * @see #unregisterListener(SensorEventListener, Sensor)
      *
-     * @throws IllegalArgumentException when sensor is null or a trigger sensor
      */
     public boolean registerListener(SensorEventListener listener, Sensor sensor, int rateUs) {
         return registerListener(listener, sensor, rateUs, null);
@@ -638,7 +635,9 @@
      * {@link #requestTriggerSensor(TriggerEventListener, Sensor)} instead. </p>
      *
      * @param listener A {@link android.hardware.SensorEventListener SensorEventListener} object
-     *            that will receive the sensor events.
+     *            that will receive the sensor events. If the application is interested in receiving
+     *            flush complete notifications, it should register with
+     *            {@link android.hardware.SensorEventListener SensorEventListener2} instead.
      * @param sensor The {@link android.hardware.Sensor Sensor} to register to.
      * @param rateUs The desired delay between two consecutive events in microseconds. This is only
      *            a hint to the system. Events may be received faster or slower than the specified
@@ -651,30 +650,23 @@
      *            large. If this is set to zero, batch mode is disabled and events are delivered in
      *            continuous mode as soon as they are available which is equivalent to calling
      *            {@link #registerListener(SensorEventListener, Sensor, int)}.
-     * @param reservedFlags Always set to Zero.
-     * @param flushCompleteListener A {@link android.hardware.FlushCompleteListener
-     *            FlushCompleteListener} object which is called when any application calls flush()
-     *            on this sensor and all the events in the batch at the time of calling flush() are
-     *            successfully delivered to the listeners.
-     * @return true if batch mode is successfully enabled for this sensor, false otherwise.
+     * @return <code>true</code> if batch mode is successfully enabled for this sensor,
+     *         <code>false</code> otherwise.
      * @see #registerListener(SensorEventListener, Sensor, int)
      * @see #unregisterListener(SensorEventListener)
-     * @see #flush(Sensor)
-     * @throws IllegalArgumentException when sensor or listener is null or a trigger sensor.
+     * @see #flush(SensorEventListener)
      */
     public boolean registerListener(SensorEventListener listener, Sensor sensor, int rateUs,
-            int maxBatchReportLatencyUs, int reservedFlags,
-            FlushCompleteListener flushCompleteListener) {
+            int maxBatchReportLatencyUs) {
         int delay = getDelay(rateUs);
-        return registerListenerImpl(listener, sensor, delay, null, maxBatchReportLatencyUs,
-                                        reservedFlags, flushCompleteListener);
+        return registerListenerImpl(listener, sensor, delay, null, maxBatchReportLatencyUs, 0);
     }
 
     /**
      * Registers a {@link android.hardware.SensorEventListener SensorEventListener} for the given
      * sensor. Events are delivered in continuous mode as soon as they are available. To reduce the
-     * battery usage, use {@link #registerListener(SensorEventListener, Sensor, int, int, int,
-     * FlushCompleteListener)} which enables batch mode for the sensor.
+     * battery usage, use {@link #registerListener(SensorEventListener, Sensor, int, int)} which
+     * enables batch mode for the sensor.
      *
      * <p class="note"></p>
      * Note: Don't use this method with a one shot trigger sensor such as
@@ -706,67 +698,80 @@
      *        {@link android.hardware.SensorEvent sensor events} will be
      *        delivered to.
      *
-     * @return true if the sensor is supported and successfully enabled.
+     * @return <code>true</code> if the sensor is supported and successfully enabled.
      *
      * @see #registerListener(SensorEventListener, Sensor, int)
      * @see #unregisterListener(SensorEventListener)
      * @see #unregisterListener(SensorEventListener, Sensor)
-     *
-     * @throws IllegalArgumentException when sensor is null or a trigger sensor
      */
     public boolean registerListener(SensorEventListener listener, Sensor sensor, int rateUs,
             Handler handler) {
-        if (listener == null || sensor == null) {
-            return false;
-        }
-
         int delay = getDelay(rateUs);
-        return registerListenerImpl(listener, sensor, delay, handler, 0, 0, null);
+        return registerListenerImpl(listener, sensor, delay, handler, 0, 0);
     }
 
     /**
      * Enables batch mode for a sensor with the given rate and maxBatchReportLatency.
-     * @param handler
-     *        The {@link android.os.Handler Handler} the
-     *        {@link android.hardware.SensorEvent sensor events} will be
-     *        delivered to.
+     * @param listener A {@link android.hardware.SensorEventListener SensorEventListener} object
+     *            that will receive the sensor events. If the application is interested in receiving
+     *            flush complete notifications, it should register with
+     *            {@link android.hardware.SensorEventListener SensorEventListener2} instead.
+     * @param sensor The {@link android.hardware.Sensor Sensor} to register to.
+     * @param rateUs The desired delay between two consecutive events in microseconds. This is only
+     *            a hint to the system. Events may be received faster or slower than the specified
+     *            rate. Usually events are received faster. Can be one of
+     *            {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
+     *            {@link #SENSOR_DELAY_GAME}, {@link #SENSOR_DELAY_FASTEST} or the delay in
+     *            microseconds.
+     * @param maxBatchReportLatencyUs An event in the batch can be delayed by at most
+     *            maxBatchReportLatency microseconds. More events can be batched if this value is
+     *            large. If this is set to zero, batch mode is disabled and events are delivered in
+     *            continuous mode as soon as they are available which is equivalent to calling
+     *            {@link #registerListener(SensorEventListener, Sensor, int)}.
+     * @param handler The {@link android.os.Handler Handler} the
+     *        {@link android.hardware.SensorEvent sensor events} will be delivered to.
      *
-     * @see #registerListener(SensorEventListener, Sensor, int, int, int, FlushCompleteListener)
+     * @return <code>true</code> if batch mode is successfully enabled for this sensor,
+     *         <code>false</code> otherwise.
+     * @see #registerListener(SensorEventListener, Sensor, int, int)
      */
     public boolean registerListener(SensorEventListener listener, Sensor sensor, int rateUs,
-            int maxBatchReportLatencyUs, int reservedFlags, Handler handler,
-            FlushCompleteListener flushCompleteListener) {
+            int maxBatchReportLatencyUs, Handler handler) {
         int delayUs = getDelay(rateUs);
-        return registerListenerImpl(listener, sensor, delayUs, handler, maxBatchReportLatencyUs,
-                                        reservedFlags, flushCompleteListener);
+        return registerListenerImpl(listener, sensor, delayUs, handler, maxBatchReportLatencyUs, 0);
     }
 
     /** @hide */
     protected abstract boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
-            int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags,
-            FlushCompleteListener flushCompleteListener);
+            int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags);
 
 
     /**
-     * Flushes the batch FIFO of the given sensor. If there are events in the FIFO of this sensor,
-     * they are returned as if the batch timeout has expired. Events are returned in the
-     * usual way through the SensorEventListener. This call doesn't effect the batch timeout for
-     * this sensor. This call is asynchronous and returns immediately. FlushCompleteListener is
-     * called after all the events in the batch at the time of calling this method have been
-     * delivered successfully.
-     * @param sensor
-     *        The {@link android.hardware.Sensor Sensor} to flush.
-     * @return true if the flush is initiated successfully. false if the sensor isn't active
-     *         i.e no application is registered for updates from this sensor.
-     * @see #registerListener(SensorEventListener, Sensor, int, int, int, FlushCompleteListener)
-     * @throws IllegalArgumentException when sensor is null or a trigger sensor.
+     * Flushes the batch FIFO of all the sensors registered for this listener. If there are events
+     * in the FIFO of the sensor, they are returned as if the batch timeout in the FIFO of the
+     * sensors had expired. Events are returned in the usual way through the SensorEventListener.
+     * This call doesn't affect the batch timeout for this sensor. This call is asynchronous and
+     * returns immediately.
+     * {@link android.hardware.SensorEventListener2#onFlushCompleted onFlushCompleted} is called
+     * after all the events in the batch at the time of calling this method have been delivered
+     * successfully. If the hardware doesn't support flush, it still returns true and a trivial
+     * flush complete event is sent after the current event for all the clients registered for this
+     * sensor.
+     *
+     * @param listener A {@link android.hardware.SensorEventListener SensorEventListener} object
+     *        which was previously used in a registerListener call.
+     * @return <code>true</code> if the flush is initiated successfully on all the sensors
+     *         registered for this listener, false if no sensor is previously registered for this
+     *         listener or flush on one of the sensors fails.
+     * @see #registerListener(SensorEventListener, Sensor, int, int)
+     * @throws IllegalArgumentException when listener is null.
      */
-    public boolean flush(Sensor sensor) {
-        return flushImpl(sensor);
+    public boolean flush(SensorEventListener listener) {
+        return flushImpl(listener);
     }
 
     /** @hide */
-    protected abstract boolean flushImpl(Sensor sensor);
+    protected abstract boolean flushImpl(SensorEventListener listener);
 
     /**
      * <p>
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 9747f0d..14f67c5 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -93,17 +93,20 @@
     /** @hide */
     @Override
     protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
-            int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags,
-            FlushCompleteListener flushCompleteListener) {
-        if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
-        if (listener == null) throw new IllegalArgumentException("listener cannot be null");
-        if (reservedFlags != 0) throw new IllegalArgumentException("reservedFlags should be zero");
-        if (delayUs < 0) throw new IllegalArgumentException("rateUs should be positive");
-        if (maxBatchReportLatencyUs < 0)
-            throw new IllegalArgumentException("maxBatchReportLatencyUs should be positive");
+            int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags) {
+        if (listener == null || sensor == null) {
+            Log.e(TAG, "sensor or listener is null");
+            return false;
+        }
         // Trigger Sensors should use the requestTriggerSensor call.
-        if (Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT)
-            throw new IllegalArgumentException("Trigger Sensors cannot use registerListener");
+        if (Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) {
+            Log.e(TAG, "Trigger Sensors should use the requestTriggerSensor.");
+            return false;
+        }
+        if (maxBatchReportLatencyUs < 0 || delayUs < 0) {
+            Log.e(TAG, "maxBatchReportLatencyUs and delayUs should be non-negative");
+            return false;
+        }
 
         // Invariants to preserve:
         // - one Looper per SensorEventListener
@@ -113,7 +116,7 @@
             SensorEventQueue queue = mSensorListeners.get(listener);
             if (queue == null) {
                 Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
-                queue = new SensorEventQueue(listener, looper, this, flushCompleteListener);
+                queue = new SensorEventQueue(listener, looper, this);
                 if (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags)) {
                     queue.dispose();
                     return false;
@@ -200,16 +203,17 @@
         }
     }
 
-    protected boolean flushImpl(Sensor sensor) {
-        if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
-        if(Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT)
-            throw new IllegalArgumentException("Trigger Sensors cannot call flush");
+    protected boolean flushImpl(SensorEventListener listener) {
+        if (listener == null) throw new IllegalArgumentException("listener cannot be null");
 
-        FlushEventQueue queue = new FlushEventQueue(mMainLooper, this);
-        if (queue.flushSensor(sensor) != 0) {
-            return false;
+        synchronized (mSensorListeners) {
+            SensorEventQueue queue = mSensorListeners.get(listener);
+            if (queue == null) {
+                return false;
+            } else {
+                return (queue.flush() == 0);
+            }
         }
-        return true;
     }
 
     /*
@@ -224,7 +228,7 @@
                 int maxBatchReportLatencyUs, int reservedFlags);
         private static native int nativeDisableSensor(int eventQ, int handle);
         private static native void nativeDestroySensorEventQueue(int eventQ);
-        private static native int nativeFlushSensor(int eventQ, int handle);
+        private static native int nativeFlushSensor(int eventQ);
         private int nSensorEventQueue;
         private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
         protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
@@ -291,10 +295,9 @@
             return false;
         }
 
-        public int flushSensor(Sensor sensor) {
+        public int flush() {
             if (nSensorEventQueue == 0) throw new NullPointerException();
-            if (sensor == null) throw new NullPointerException();
-            return nativeFlushSensor(nSensorEventQueue, sensor.getHandle());
+            return nativeFlushSensor(nSensorEventQueue);
         }
 
         public boolean hasSensors() {
@@ -347,14 +350,12 @@
 
     static final class SensorEventQueue extends BaseEventQueue {
         private final SensorEventListener mListener;
-        private final FlushCompleteListener mFlushCompleteListener;
         private final SparseArray<SensorEvent> mSensorsEvents = new SparseArray<SensorEvent>();
 
         public SensorEventQueue(SensorEventListener listener, Looper looper,
-                SystemSensorManager manager, FlushCompleteListener flushCompleteListener) {
+                SystemSensorManager manager) {
             super(looper, manager);
             mListener = listener;
-            mFlushCompleteListener = flushCompleteListener;
         }
 
         public void addSensorEvent(Sensor sensor) {
@@ -408,9 +409,9 @@
 
         @SuppressWarnings("unused")
         protected void dispatchFlushCompleteEvent(int handle) {
-            final Sensor sensor = sHandleToSensor.get(handle);
-            if (mFlushCompleteListener != null) {
-                mFlushCompleteListener.onFlushCompleted(sensor);
+            if (mListener instanceof SensorEventListener2) {
+                final Sensor sensor = sHandleToSensor.get(handle);
+                ((SensorEventListener2)mListener).onFlushCompleted(sensor);
             }
             return;
         }
@@ -464,30 +465,4 @@
         protected void dispatchFlushCompleteEvent(int handle) {
         }
     }
-
-    static final class FlushEventQueue extends BaseEventQueue {
-        public FlushEventQueue(Looper looper, SystemSensorManager manager) {
-            super(looper, manager);
-        }
-
-        @SuppressWarnings("unused")
-        @Override
-        protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
-                long timestamp) {
-        }
-
-        @Override
-        @SuppressWarnings("unused")
-        protected void addSensorEvent(Sensor sensor) {
-        }
-
-        @Override
-        @SuppressWarnings("unused")
-        protected void removeSensorEvent(Sensor sensor) {
-        }
-
-        @SuppressWarnings("unused")
-        protected void dispatchFlushCompleteEvent(int handle) {
-        }
-    }
 }
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index 23ed019..44e7ec1 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -160,8 +160,7 @@
     public View getItemView(final MenuItemImpl item, View convertView, ViewGroup parent) {
         View actionView = item.getActionView();
         if (actionView == null || item.hasCollapsibleActionView()) {
-            // Don't recycle existing item views for action buttons; it interferes with transitions.
-            actionView = super.getItemView(item, null, parent);
+            actionView = super.getItemView(item, convertView, parent);
         }
         actionView.setVisibility(item.isActionViewExpanded() ? View.GONE : View.VISIBLE);
 
diff --git a/core/java/com/android/internal/widget/SubtitleView.java b/core/java/com/android/internal/widget/SubtitleView.java
index ccedf63..071193c 100644
--- a/core/java/com/android/internal/widget/SubtitleView.java
+++ b/core/java/com/android/internal/widget/SubtitleView.java
@@ -180,7 +180,9 @@
             mInnerPaddingX = (int) (size * INNER_PADDING_RATIO + 0.5f);
 
             mHasMeasurements = false;
-            forceLayout();
+
+            requestLayout();
+            invalidate();
         }
     }
 
@@ -189,7 +191,9 @@
             mTextPaint.setTypeface(typeface);
 
             mHasMeasurements = false;
-            forceLayout();
+
+            requestLayout();
+            invalidate();
         }
     }
 
@@ -198,7 +202,9 @@
             mAlignment = textAlignment;
 
             mHasMeasurements = false;
-            forceLayout();
+
+            requestLayout();
+            invalidate();
         }
     }
 
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 4290a6e..793d1bf 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -220,9 +220,9 @@
     receiver->decStrong((void*)nativeInitSensorEventQueue);
 }
 
-static jint nativeFlushSensor(JNIEnv *env, jclass clazz, jint eventQ, jint handle) {
+static jint nativeFlushSensor(JNIEnv *env, jclass clazz, jint eventQ) {
     sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
-    return receiver->getSensorEventQueue()->flushSensor(handle);
+    return receiver->getSensorEventQueue()->flush();
 }
 
 //----------------------------------------------------------------------------
@@ -255,7 +255,7 @@
             (void*)nativeDestroySensorEventQueue },
 
     {"nativeFlushSensor",
-            "(II)I",
+            "(I)I",
             (void*)nativeFlushSensor },
 };
 
diff --git a/docs/html/distribute/googleplay/spotlight/games.jd b/docs/html/distribute/googleplay/spotlight/games.jd
index 4e356db..1fbc03f 100644
--- a/docs/html/distribute/googleplay/spotlight/games.jd
+++ b/docs/html/distribute/googleplay/spotlight/games.jd
@@ -102,7 +102,7 @@
   width: 78px;
   float: left;
   margin: 12px 20px 30px 20px;"
-  src="//lh5.ggpht.com/l20dR2HYLV8vECoC35q_0NdfaAGTe4lZIFy_wCJRDqZjeQqSgneLRpXi3qOnnCaLXA=w124">
+  src="//lh4.ggpht.com/Q7mQJsdhulW4_s039R9aaRhQkGnyzLkhF00j5EnyhHOivijnyi7P7b5A8qG0xk1r-jQ=w124">
           
 <div style="list-style: none;height:100%;
   float: right;
diff --git a/docs/html/distribute/googleplay/spotlight/tablets.jd b/docs/html/distribute/googleplay/spotlight/tablets.jd
index cfea29a..7a98755 100644
--- a/docs/html/distribute/googleplay/spotlight/tablets.jd
+++ b/docs/html/distribute/googleplay/spotlight/tablets.jd
@@ -17,7 +17,90 @@
 expand their offering to include Android tablets.</p>
 
 
-<div style="margin-bottom:2em;"><!-- START STORY -->
+<div style="margin-bottom:2em;" id="rememberthemilk"><!-- START STORY -->
+
+<h3>Remember The Milk: Lifting installs with tablet design</h3>
+
+  <img alt="" class="screenshot thumbnail" style="-webkit-border-radius: 5px;
+            -moz-border-radius: 5px;
+            border-radius: 5px height:78px;
+            width: 78px;
+            float: left;
+            margin: 12px 20px 9px 20px;" src=
+            "//lh3.ggpht.com/xmnal18taauP2mjQFEhr1PhcItQ_W32IRuaD86IoL2U_4E-mfeKiliKtkISgOuA6Ln9n=w124">
+          
+  <div style="list-style: none;height:100%;
+  float: right;
+  border-top: 1px solid #9C0;
+  width: 220px;
+  margin: 4px 20px;padding: .5em;">
+  
+
+    <h5>About the app</h5> 
+    
+    
+    <ul>
+      <li><a href="//play.google.com/store/apps/details?id=com.rememberthemilk.MobileRTM">Remember The Milk</a></li>
+      <li>A feature-packed to-do list app; never forget the milk (or anything else) again</li>
+    </ul>
+
+    <h5>Tablet Results</h5> 
+
+    <ul>
+      <li>83% jump in tablet installs following update </li>
+      <li>Nexus 7 is most popular Android device for app </li>
+      <li>Single APK for phones and tablets</li>
+      </ul>
+    
+    <div style="padding:.5em 0 0 1em;">
+      <a href="//play.google.com/store/apps/details?id=com.rememberthemilk.MobileRTM">
+        <img alt="Android app on Google Play"
+         src="//developer.android.com/images/brand/en_generic_rgb_wo_45.png" />
+      </a>
+      
+    </div>
+  </div>
+
+  <div style="line-height:1.4em;">
+    <p style="margin-top:0;margin-bottom:12px;">When the Android tablet guidelines
+      came out in 2012, the team at Remember The Milk had already been thinking about
+      a redesign for their <a href="//play.google.com/store/apps/details?id=com.rememberthemilk.MobileRTM">feature-packed
+      to-do list app</a>. Omar Kilani, Co-founder of Remember The Milk, explains how
+      <a href="//blog.rememberthemilk.com/2013/04/the-all-new-remember-the-milk-for-android-and-tablets-too/">updating</a>
+      their app to meet the tablet guidelines lead to an 83% jump in tablet installs: </p>
+
+    <p>“We took this as an opportunity to think about how we were going to approach
+      Android tablets differently from a user experience perspective. The guidelines
+      were a helpful resource, and with the extra screen real estate tablets afford,
+      users have the opportunity to see all of their data in context and drill down
+      on more items. All of this is accomplished using a single APK on Play, even though
+      the phone and tablet versions each capture completely different use cases for us.”</p>
+
+    <p>“In the month after updating, we saw our tablet installs on Google Play jump 83%,
+      and the Nexus 7 is now the most popular Android device amongst our users. For us,
+      designing for tablets was an investment that has really paid off.”</p>
+
+    <p>The team also came out with a number of other goodies &mdash; including a new set of
+      widgets and richer notifications, and more ways to provide an immersive experience
+      for their users.</p>
+  </div>
+
+  <div style="clear:both;margin-top:30px;width:auto;">
+  
+    <img src="{@docRoot}images/distribute/rememberthemilk.png">
+
+    <div style="width:600px;margin-top:0px;padding:0 90px;">
+      <p class="image-caption"><span style="font-weight:500;">Tablet redesign led to lift
+      in installs</span>: Following the redesign of the Android app, in part to meet the tablet
+      design criteria, Remember The Milk saw an 83% increase in tablet installs.</p>
+    </div>
+
+  </div>
+
+</div> <!-- END STORY -->
+
+
+<div style="margin-bottom:2em;" id="mint"><!-- START STORY -->
 
 <h3>Mint: More screen real estate = more engagement</h3>
 
@@ -96,7 +179,7 @@
 
   <div style="clear:both;margin-top:40px;width:auto;">
   
-    <a href=""><img src="{@docRoot}images/distribute/mint.png"></a>
+    <img src="{@docRoot}images/distribute/mint.png">
 
     <div style="width:600px;margin-top:0px;padding:0 90px;">
       <p class="image-caption"><span style="font-weight:500;">Making the most of tablet screens</span>: Mint used the extra screen area on tablets to offer quick access to additional tools and information.</p>
@@ -184,7 +267,7 @@
 
   <div style="clear:both;margin-top:40px;width:auto;">
   
-    <a href=""><img src="{@docRoot}images/distribute/tinyvillage.png"></a>
+    <img src="{@docRoot}images/distribute/tinyvillage.png">
 
     <div style="width:600px;margin-top:0px;padding:0 90px;">
       <p class="image-caption"><span style="font-weight:500;">More monetization
@@ -268,7 +351,7 @@
 
   <div style="clear:both;margin-top:40px;width:auto;">
   
-    <a href=""><img src="{@docRoot}images/distribute/instapaper.png"></a>
+    <img src="{@docRoot}images/distribute/instapaper.png">
 
     <div style="width:600px;margin-top:0px;padding:0 90px;">
       <p class="image-caption"><span style="font-weight:500;">Popular with
diff --git a/docs/html/images/distribute/rememberthemilk.png b/docs/html/images/distribute/rememberthemilk.png
new file mode 100644
index 0000000..b170cf3
--- /dev/null
+++ b/docs/html/images/distribute/rememberthemilk.png
Binary files differ
diff --git a/docs/html/training/articles/security-tips.jd b/docs/html/training/articles/security-tips.jd
index 1ac56b9..54aebac 100644
--- a/docs/html/training/articles/security-tips.jd
+++ b/docs/html/training/articles/security-tips.jd
@@ -553,7 +553,7 @@
 or an explicit intent to a specific application component.</p>
 
 <p>Note that ordered broadcasts can be “consumed” by a recipient, so they
-may not be delivered to all applications.  If you are sending an intent that muse be delivered
+may not be delivered to all applications.  If you are sending an intent that must be delivered
 to a specific receiver, then you must use an explicit intent that declares the receiver
 by nameintent.</p>
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
index d1862cd..7039218 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java
@@ -17,12 +17,14 @@
 
 import android.os.Handler;
 import android.os.Looper;
+import android.util.Log;
 import android.view.View;
 
 public class KeyguardViewStateManager implements
         SlidingChallengeLayout.OnChallengeScrolledListener,
         ChallengeLayout.OnBouncerStateChangedListener {
 
+    private static final String TAG = "KeyguardViewStateManager";
     private KeyguardWidgetPager mKeyguardWidgetPager;
     private ChallengeLayout mChallengeLayout;
     private KeyguardHostView mKeyguardHostView;
@@ -100,18 +102,20 @@
     }
 
     public void fadeOutSecurity(int duration) {
-        ((View) mKeyguardSecurityContainer).animate().alpha(0).setDuration(duration);
+        ((View) mKeyguardSecurityContainer).animate().alpha(0f).setDuration(duration).start();
     }
 
     public void fadeInSecurity(int duration) {
-        ((View) mKeyguardSecurityContainer).animate().alpha(1f).setDuration(duration);
+        ((View) mKeyguardSecurityContainer).animate().alpha(1f).setDuration(duration).start();
     }
 
     public void onPageBeginMoving() {
         if (mChallengeLayout.isChallengeOverlapping() &&
                 mChallengeLayout instanceof SlidingChallengeLayout) {
             SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
-            scl.fadeOutChallenge();
+            if (!mKeyguardWidgetPager.isWarping()) {
+                scl.fadeOutChallenge();
+            }
             mPageIndexOnPageBeginMoving = mKeyguardWidgetPager.getCurrentPage();
         }
         // We use mAppWidgetToShow to show a particular widget after you add it--
@@ -133,7 +137,9 @@
     public void onPageSwitching(View newPage, int newPageIndex) {
         if (mKeyguardWidgetPager != null && mChallengeLayout instanceof SlidingChallengeLayout) {
             boolean isCameraPage = newPage instanceof CameraWidgetFrame;
-            ((SlidingChallengeLayout) mChallengeLayout).setChallengeInteractive(!isCameraPage);
+            SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
+            scl.setChallengeInteractive(!isCameraPage);
+            if (isCameraPage) scl.fadeOutChallenge();
         }
 
         // If the page we're settling to is the same as we started on, and the action of
@@ -174,13 +180,15 @@
     }
 
     public void onPageBeginWarp() {
-        // fadeOutSecurity(WARP_FADE_DURATION);
-        // mKeyguardWidgetPager.showNonWarpViews(WARP_FADE_DURATION, false);
+        fadeOutSecurity(SlidingChallengeLayout.CHALLENGE_FADE_OUT_DURATION);
+        View frame = mKeyguardWidgetPager.getPageAt(mKeyguardWidgetPager.getPageWarpIndex());
+        ((KeyguardWidgetFrame)frame).showFrame(this);
     }
 
     public void onPageEndWarp() {
-        // fadeInSecurity(WARP_FADE_DURATION);
-        // mKeyguardWidgetPager.showNonWarpViews(WARP_FADE_DURATION, true);
+        fadeInSecurity(SlidingChallengeLayout.CHALLENGE_FADE_IN_DURATION);
+        View frame = mKeyguardWidgetPager.getPageAt(mKeyguardWidgetPager.getPageWarpIndex());
+        ((KeyguardWidgetFrame)frame).hideFrame(this);
     }
 
     private int getChallengeTopRelativeToFrame(KeyguardWidgetFrame frame, int top) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
index f8857ab..704af6e 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
@@ -188,11 +188,13 @@
 
     @Override
     public void onPageBeginWarp() {
+        showOutlinesAndSidePages();
         mViewStateManager.onPageBeginWarp();
     }
 
     @Override
     public void onPageEndWarp() {
+        hideOutlinesAndSidePages();
         mViewStateManager.onPageEndWarp();
     }
 
@@ -495,7 +497,7 @@
     }
 
     public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) {
-        if (getPageWarpIndex() != -1) {
+        if (isWarping()) {
             return index == getPageWarpIndex() ? 1.0f : 0.0f;
         }
         if (showSidePages) {
@@ -949,17 +951,17 @@
                     // to keep event dispatch happy.
                     mCameraEventInProgress = true;
                     userActivity();
-                    startWarp(cameraPage);
+                    startPageWarp(cameraPage);
                     break;
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
                     mCameraEventInProgress = false;
-                    endWarp = true;
+                    endWarp = isWarping();
                     break;
             }
             dispatchTouchEvent(event);
             // This has to happen after the event has been handled by the real widget pager
-            if (endWarp) endWarp();
+            if (endWarp) stopPageWarp();
         }
         endCameraEvent();
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/PagedView.java b/packages/Keyguard/src/com/android/keyguard/PagedView.java
index 666227c..6cf6953 100644
--- a/packages/Keyguard/src/com/android/keyguard/PagedView.java
+++ b/packages/Keyguard/src/com/android/keyguard/PagedView.java
@@ -47,6 +47,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.AccelerateInterpolator;
 import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
@@ -60,12 +61,13 @@
  * sequential list of "pages"
  */
 public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener {
+    private static final int WARP_SNAP_DURATION = 160;
     private static final String TAG = "WidgetPagedView";
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_WARP = false;
     protected static final int INVALID_PAGE = -1;
-    private static final int WARP_PEEK_ANIMATION_DURATION = 250;
-    private static final float WARP_ANIMATE_AMOUNT = -40.0f; // in dip
+    private static final int WARP_PEEK_ANIMATION_DURATION = 150;
+    private static final float WARP_ANIMATE_AMOUNT = -75.0f; // in dip
 
     // the min drag distance for a fling to register, to prevent random page shifts
     private static final int MIN_LENGTH_FOR_FLING = 25;
@@ -261,6 +263,8 @@
 
     private boolean mIsCameraEvent;
     private float mWarpPeekAmount;
+    private boolean mAnimatingWarp; // true while warped page is being animated
+    private boolean mFingerDown;
 
     public interface PageSwitchListener {
         void onPageSwitching(View newPage, int newPageIndex);
@@ -484,7 +488,7 @@
         if (DEBUG_WARP) Log.v(TAG, "pageBeginMoving(" + mIsPageMoving + ")");
         if (!mIsPageMoving) {
             mIsPageMoving = true;
-            if (mPageWarpIndex != -1) {
+            if (isWarping()) {
                 onPageBeginWarp();
                 if (mPageSwapIndex != -1) {
                     swapPages(mPageSwapIndex, mPageWarpIndex);
@@ -498,12 +502,12 @@
         if (DEBUG_WARP) Log.v(TAG, "pageEndMoving(" + mIsPageMoving + ")");
         if (mIsPageMoving) {
             mIsPageMoving = false;
-            if (mPageWarpIndex != -1) {
+            if (isWarping()) {
                 if (mPageSwapIndex != -1) {
                     swapPages(mPageSwapIndex, mPageWarpIndex);
-                    resetPageWarp();
                 }
                 onPageEndWarp();
+                resetPageWarp();
             }
             onPageEndMoving();
         }
@@ -1124,8 +1128,8 @@
             }
 
             case MotionEvent.ACTION_DOWN: {
-                if (mIsCameraEvent) {
-                    animateWarpPageOnScreen();
+                if (mIsCameraEvent && !mAnimatingWarp) {
+                    animateWarpPageOnScreen("interceptTouch(): DOWN");
                 }
                 // Remember where the motion event started
                 saveDownState(ev);
@@ -1219,6 +1223,8 @@
         mTotalMotionX = 0;
         mActivePointerId = ev.getPointerId(0);
 
+        mFingerDown = true;
+
         // Determine if the down event is within the threshold to be an edge swipe
         int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize;
         int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize;
@@ -1393,8 +1399,10 @@
 
             if (mTouchState == TOUCH_STATE_SCROLLING) {
                 pageBeginMoving();
-            } else {
-                animateWarpPageOnScreen();
+            }
+
+            if (mIsCameraEvent && !mAnimatingWarp) {
+                animateWarpPageOnScreen("onTouch(): DOWN");
             }
             break;
 
@@ -1571,7 +1579,8 @@
                 // move to the left and fling to the right will register as a fling to the right.
                 if (((isSignificantMove && deltaX > 0 && !isFling) ||
                         (isFling && velocityX > 0)) && mCurrentPage > 0) {
-                    finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
+                    finalPage = returnToOriginalPage || isWarping()
+                            ? mCurrentPage : mCurrentPage - 1;
                     snapToPageWithVelocity(finalPage, velocityX);
                 } else if (((isSignificantMove && deltaX < 0 && !isFling) ||
                         (isFling && velocityX < 0)) &&
@@ -1661,6 +1670,7 @@
         setTouchState(TOUCH_STATE_REST);
         mActivePointerId = INVALID_POINTER;
         mDownEventOnEdge = false;
+        mFingerDown = false;
     }
 
     protected void onUnhandledTap(MotionEvent ev) {}
@@ -1790,7 +1800,14 @@
     }
 
     protected void snapToDestination() {
-        snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
+        if (isWarping()) {
+            cancelWarpAnimation("snapToDestination");
+        }
+        snapToPage(getPageNearestToCenterOfScreen(), getPageSnapDuration());
+    }
+
+    private int getPageSnapDuration() {
+        return isWarping() ? WARP_SNAP_DURATION : PAGE_SNAP_ANIMATION_DURATION;
     }
 
     private static class ScrollInterpolator implements Interpolator {
@@ -1817,6 +1834,10 @@
         whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
         int halfScreenSize = getViewportWidth() / 2;
 
+        if (isWarping()) {
+            cancelWarpAnimation("snapToPageWithVelocity");
+        }
+
         if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
         if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): "
                 + getViewportWidth() + ", " + getChildWidth(whichPage));
@@ -1827,7 +1848,7 @@
         if (Math.abs(velocity) < mMinFlingVelocity) {
             // If the velocity is low enough, then treat this more as an automatic page advance
             // as opposed to an apparent physical response to flinging
-            snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+            snapToPage(whichPage, getPageSnapDuration());
             return;
         }
 
@@ -1851,10 +1872,10 @@
     }
 
     protected void snapToPage(int whichPage) {
-        snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+        snapToPage(whichPage, getPageSnapDuration());
     }
     protected void snapToPageImmediately(int whichPage) {
-        snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true);
+        snapToPage(whichPage, getPageSnapDuration(), true);
     }
 
     protected void snapToPage(int whichPage, int duration) {
@@ -1884,8 +1905,8 @@
             mNextPage = whichPage;
         }
 
-        if (mPageWarpIndex != -1) {
-            animateWarpPageOffScreen();
+        if (isWarping()) {
+            onPageEndWarp();
             resetPageWarp();
         }
 
@@ -1918,6 +1939,10 @@
         invalidate();
     }
 
+    protected boolean isWarping() {
+        return mPageWarpIndex != -1;
+    }
+
     public void scrollLeft() {
         if (mScroller.isFinished()) {
             if (mCurrentPage > 0) snapToPage(mCurrentPage - 1);
@@ -2650,21 +2675,53 @@
         mIsCameraEvent = false;
     }
 
-    private void animateWarpPageOnScreen() {
-        if (DEBUG_WARP) Log.v(TAG, "animateWarpPageOnScreen()");
-        if (mPageWarpIndex != -1) {
+    AnimatorListenerAdapter mFinishWarpAnimationListener = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mAnimatingWarp = false;
+            if (!mFingerDown) {
+                animateWarpPageOffScreen("animation end", true);
+            }
+        }
+    };
+
+    private void cancelWarpAnimation(String msg) {
+        if (DEBUG_WARP) Log.v(TAG, "cancelWarpAnimation(" + msg + ")");
+        // We're done with the animation, let the scroller take over the positioning
+        KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(mPageWarpIndex);
+        v.animate().cancel();
+        v.setTranslationX(0f);
+        scrollBy((int) Math.round(v.getTranslationX() - mWarpPeekAmount), 0);
+    }
+
+    private void animateWarpPageOnScreen(String reason) {
+        if (DEBUG_WARP) Log.v(TAG, "animateWarpPageOnScreen(" + reason + ")");
+        if (isWarping()) {
+            onPageBeginWarp();
             KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(mPageWarpIndex);
             if (DEBUG_WARP) Log.v(TAG, "moving page on screen: Tx=" + v.getTranslationX());
-            v.animate().translationX(mWarpPeekAmount).setDuration(WARP_PEEK_ANIMATION_DURATION);
+            DecelerateInterpolator interp = new DecelerateInterpolator(1.5f);
+            v.animate().translationX(mWarpPeekAmount)
+                    .setInterpolator(interp)
+                    .setDuration(WARP_PEEK_ANIMATION_DURATION)
+                    .setListener(mFinishWarpAnimationListener);
+            mAnimatingWarp = true;
         }
     }
 
-    private void animateWarpPageOffScreen() {
-        if (DEBUG_WARP) Log.v(TAG, "animateWarpPageOffScreen()");
-        if (mPageWarpIndex != -1) {
+    private void animateWarpPageOffScreen(String reason, boolean animate) {
+        if (DEBUG_WARP) Log.v(TAG, "animateWarpPageOffScreen(" + reason + " anim:" + animate + ")");
+        if (isWarping()) {
+            onPageEndWarp();
             KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(mPageWarpIndex);
             if (DEBUG_WARP) Log.v(TAG, "moving page off screen: Tx=" + v.getTranslationX());
-            v.animate().translationX(0.0f).setDuration(WARP_PEEK_ANIMATION_DURATION);
+            AccelerateInterpolator interp = new AccelerateInterpolator(1.5f);
+            v.animate().translationX(0.0f)
+                    .setInterpolator(interp)
+                    .setDuration(animate ? WARP_PEEK_ANIMATION_DURATION : 0)
+                    .setListener(null);
+        } else {
+            if (DEBUG_WARP) Log.e(TAG, "animateWarpPageOffScreen(): not warping", new Exception());
         }
     }
 
@@ -2681,7 +2738,7 @@
         }
     }
 
-    public void startWarp(int pageIndex) {
+    public void startPageWarp(int pageIndex) {
         if (DEBUG_WARP) Log.v(TAG, "START WARP");
         if (pageIndex != mCurrentPage + 1) {
             mPageSwapIndex = mCurrentPage + 1;
@@ -2693,7 +2750,7 @@
         return mPageWarpIndex;
     }
 
-    public void endWarp() {
+    public void stopPageWarp() {
         if (DEBUG_WARP) Log.v(TAG, "END WARP");
         // mPageSwapIndex is reset in snapToPage() after the scroll animation completes
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java b/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java
index 5e7816c..1036c83 100644
--- a/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java
+++ b/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java
@@ -88,8 +88,8 @@
     public static final int SCROLL_STATE_SETTLING = 2;
     public static final int SCROLL_STATE_FADING = 3;
 
-    private static final int CHALLENGE_FADE_OUT_DURATION = 100;
-    private static final int CHALLENGE_FADE_IN_DURATION = 160;
+    public static final int CHALLENGE_FADE_OUT_DURATION = 100;
+    public static final int CHALLENGE_FADE_IN_DURATION = 160;
 
     private static final int MAX_SETTLE_DURATION = 600; // ms
 
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml
index 98b5cfe..d503216 100644
--- a/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml
@@ -15,9 +15,12 @@
 -->
 
 <com.android.printspooler.PrintDialogFrame xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/content_container"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center"
-    android:background="@color/container_background">
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+    <FrameLayout
+        android:id="@+id/content_container"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:background="@color/container_background">
+    </FrameLayout>
 </com.android.printspooler.PrintDialogFrame>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintDialogFrame.java b/packages/PrintSpooler/src/com/android/printspooler/PrintDialogFrame.java
index 6dd8aa0a..c1c4d21 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintDialogFrame.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintDialogFrame.java
@@ -24,6 +24,8 @@
 
     public final int mMaxWidth;
 
+    public int mHeight;
+
     public PrintDialogFrame(Context context, AttributeSet attrs) {
         super(context, attrs);
         mMaxWidth = context.getResources().getDimensionPixelSize(
@@ -32,13 +34,36 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        if (widthMode == MeasureSpec.AT_MOST) {
-            final int receivedWidth = MeasureSpec.getSize(widthMeasureSpec);
-            final int computedWidth = Math.min(mMaxWidth, receivedWidth);
-            widthMeasureSpec = MeasureSpec.makeMeasureSpec(computedWidth,
-                    MeasureSpec.EXACTLY);
-        }
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        int measuredWidth  = getMeasuredWidth();
+        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        switch (widthMode) {
+            case MeasureSpec.UNSPECIFIED: {
+                measuredWidth = mMaxWidth;
+            } break;
+
+            case MeasureSpec.AT_MOST: {
+                final int receivedWidth = MeasureSpec.getSize(widthMeasureSpec);
+                measuredWidth = Math.min(mMaxWidth, receivedWidth);
+            } break;
+        }
+
+        mHeight = Math.max(mHeight, getMeasuredHeight());
+
+        int measuredHeight  = getMeasuredHeight();
+        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        switch (heightMode) {
+            case MeasureSpec.UNSPECIFIED: {
+                measuredHeight = mHeight;
+            } break;
+
+             case MeasureSpec.AT_MOST: {
+                final int receivedHeight = MeasureSpec.getSize(heightMeasureSpec);
+                measuredHeight = Math.min(mHeight, receivedHeight);
+            } break;
+        }
+
+        setMeasuredDimension(measuredWidth, measuredHeight);
     }
 }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index d6ebc2d..2922dd1 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -59,12 +59,14 @@
 import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.MeasureSpec;
 import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnClickListener;
+import android.view.ViewGroup.LayoutParams;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
@@ -75,6 +77,7 @@
 import android.widget.BaseAdapter;
 import android.widget.Button;
 import android.widget.EditText;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.Spinner;
 import android.widget.TextView;
@@ -1409,7 +1412,9 @@
                                         postSwitchCallback.run();
                                     }
                                 }
-                            });
+                            },
+                            new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                                    ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
                         } break;
                     }
                 } break;
@@ -1426,7 +1431,9 @@
                                         postSwitchCallback.run();
                                     }
                                 }
-                            });
+                            },
+                            new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                                    ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER));
                         } break;
                     }
                 } break;
@@ -1474,7 +1481,8 @@
             getLayoutInflater().inflate(showLayoutId, contentContainer, true);
         }
 
-        private void animateUiSwitch(int showLayoutId, final Runnable postAnimateCommand) {
+        private void animateUiSwitch(int showLayoutId, final Runnable postAnimateCommand,
+                final LayoutParams containerParams) {
             // Find everything we will shuffle around.
             final ViewGroup contentContainer = (ViewGroup) findViewById(R.id.content_container);
             final View hidingView = contentContainer.getChildAt(0);
@@ -1511,6 +1519,8 @@
                             contentContainer.setScaleY(1.0f);
                             contentContainer.addView(showingView);
 
+                            contentContainer.setLayoutParams(containerParams);
+
                             // Third animation - show the new content.
                             AutoCancellingAnimator.animate(showingView).withLayer().alpha(1.0f)
                                     .withEndAction(new Runnable() {
diff --git a/services/java/com/android/server/am/ProcessStatsService.java b/services/java/com/android/server/am/ProcessStatsService.java
index a6375e1..50a7b5c 100644
--- a/services/java/com/android/server/am/ProcessStatsService.java
+++ b/services/java/com/android/server/am/ProcessStatsService.java
@@ -473,11 +473,11 @@
                     current.setDataPosition(0);
                     ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current);
                     current.recycle();
-                    int i = 0;
-                    while (i < files.size() && (stats.mTimePeriodEndRealtime
+                    int i = files.size()-1;
+                    while (i >= 0 && (stats.mTimePeriodEndRealtime
                             - stats.mTimePeriodStartRealtime) < minTime) {
                         AtomicFile file = new AtomicFile(new File(files.get(i)));
-                        i++;
+                        i--;
                         ProcessStats moreStats = new ProcessStats(false);
                         readLocked(moreStats, file);
                         if (moreStats.mReadError == null) {
@@ -490,7 +490,7 @@
                                     - moreStats.mTimePeriodStartRealtime, sb);
                             Slog.i(TAG, sb.toString());
                         } else {
-                            Slog.w(TAG, "Failure reading " + files.get(i-1) + "; "
+                            Slog.w(TAG, "Failure reading " + files.get(i+1) + "; "
                                     + moreStats.mReadError);
                             continue;
                         }