Merge "Define audio attributes"
diff --git a/api/current.txt b/api/current.txt
index eb079db..9c142d44 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4849,6 +4849,7 @@
     method public void clearWindowAnimationFrameStats();
     method public boolean clearWindowContentFrameStats(int);
     method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
+    method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
     method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
@@ -25827,6 +25828,7 @@
     method public static boolean isatty(java.io.FileDescriptor);
     method public static void kill(int, int) throws android.system.ErrnoException;
     method public static void lchown(java.lang.String, int, int) throws android.system.ErrnoException;
+    method public static void link(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static void listen(java.io.FileDescriptor, int) throws android.system.ErrnoException;
     method public static long lseek(java.io.FileDescriptor, long, int) throws android.system.ErrnoException;
     method public static android.system.StructStat lstat(java.lang.String) throws android.system.ErrnoException;
@@ -34923,9 +34925,11 @@
     ctor public EdgeEffect(android.content.Context);
     method public boolean draw(android.graphics.Canvas);
     method public void finish();
+    method public int getMaxHeight();
     method public boolean isFinished();
     method public void onAbsorb(int);
     method public void onPull(float);
+    method public void onPull(float, float);
     method public void onRelease();
     method public void setSize(int, int);
   }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 66b82eb..b4b3c99 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3618,13 +3618,15 @@
         }
 
         // Get the primary color and update the RecentsActivityValues for this activity
-        TypedArray a = getTheme().obtainStyledAttributes(com.android.internal.R.styleable.Theme);
-        int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0);
-        a.recycle();
-        if (colorPrimary != 0) {
-            ActivityManager.RecentsActivityValues v = new ActivityManager.RecentsActivityValues();
-            v.colorPrimary = colorPrimary;
-            setRecentsActivityValues(v);
+        if (theme != null) {
+            TypedArray a = theme.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
+            int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0);
+            a.recycle();
+            if (colorPrimary != 0) {
+                ActivityManager.RecentsActivityValues v = new ActivityManager.RecentsActivityValues();
+                v.colorPrimary = colorPrimary;
+                setRecentsActivityValues(v);
+            }
         }
     }
 
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index 347de97..8ab9ac3 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -43,4 +43,5 @@
     WindowContentFrameStats getWindowContentFrameStats(int windowId);
     void clearWindowAnimationFrameStats();
     WindowAnimationFrameStats getWindowAnimationFrameStats();
+    void executeShellCommand(String command, in ParcelFileDescriptor fd);
 }
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index aab6ed8..db91742a 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -44,8 +44,8 @@
      * you to disable / reenable the keyguard.
      */
     public class KeyguardLock {
-        private IBinder mToken = new Binder();
-        private String mTag;
+        private final IBinder mToken = new Binder();
+        private final String mTag;
 
         KeyguardLock(String tag) {
             mTag = tag;
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 9405325..64e3484 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -26,6 +26,7 @@
 import android.graphics.Point;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Looper;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
@@ -40,7 +41,9 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
+import libcore.io.IoUtils;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.TimeoutException;
@@ -840,6 +843,44 @@
         return null;
     }
 
+    /**
+     * Executes a shell command. This method returs a file descriptor that points
+     * to the standard output stream. The command execution is similar to running
+     * "adb shell <command>" from a host connected to the device.
+     * <p>
+     * <strong>Note:</strong> It is your responsibility to close the retunred file
+     * descriptor once you are done reading.
+     * </p>
+     *
+     * @param command The command to execute.
+     * @return A file descriptor to the standard output stream.
+     */
+    public ParcelFileDescriptor executeShellCommand(String command) {
+        synchronized (mLock) {
+            throwIfNotConnectedLocked();
+        }
+
+        ParcelFileDescriptor source = null;
+        ParcelFileDescriptor sink = null;
+
+        try {
+            ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+            source = pipe[0];
+            sink = pipe[1];
+
+            // Calling out without a lock held.
+            mUiAutomationConnection.executeShellCommand(command, sink);
+        } catch (IOException ioe) {
+            Log.e(LOG_TAG, "Error executing shell command!", ioe);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error executing shell command!", re);
+        } finally {
+            IoUtils.closeQuietly(sink);
+        }
+
+        return source;
+    }
+
     private static float getDegreesForRotation(int value) {
         switch (value) {
             case Surface.ROTATION_90: {
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index fa40286..81bcb39 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -23,6 +23,7 @@
 import android.hardware.input.InputManager;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -33,6 +34,12 @@
 import android.view.WindowContentFrameStats;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.IAccessibilityManager;
+import libcore.io.IoUtils;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 
 /**
  * This is a remote object that is passed from the shell to an instrumentation
@@ -50,8 +57,8 @@
     private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface(
             ServiceManager.getService(Service.WINDOW_SERVICE));
 
-    private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager.Stub.asInterface(
-            ServiceManager.getService(Service.ACCESSIBILITY_SERVICE));
+    private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager.Stub
+            .asInterface(ServiceManager.getService(Service.ACCESSIBILITY_SERVICE));
 
     private final Object mLock = new Object();
 
@@ -220,6 +227,41 @@
     }
 
     @Override
+    public void executeShellCommand(String command, ParcelFileDescriptor sink)
+            throws RemoteException {
+        synchronized (mLock) {
+            throwIfCalledByNotTrustedUidLocked();
+            throwIfShutdownLocked();
+            throwIfNotConnectedLocked();
+        }
+
+        InputStream in = null;
+        OutputStream out = null;
+
+        try {
+            java.lang.Process process = Runtime.getRuntime().exec(command);
+
+            in = process.getInputStream();
+            out = new FileOutputStream(sink.getFileDescriptor());
+
+            final byte[] buffer = new byte[8192];
+            while (true) {
+                final int readByteCount = in.read(buffer);
+                if (readByteCount < 0) {
+                    break;
+                }
+                out.write(buffer, 0, readByteCount);
+            }
+        } catch (IOException ioe) {
+            throw new RuntimeException("Error running shell command", ioe);
+        } finally {
+            IoUtils.closeQuietly(in);
+            IoUtils.closeQuietly(out);
+            IoUtils.closeQuietly(sink);
+        }
+    }
+
+    @Override
     public void shutdown() {
         synchronized (mLock) {
             if (isConnectedLocked()) {
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index cec90cd..e58c54d 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -156,6 +156,9 @@
         // If true, enables automatic brightness control.
         public boolean useAutoBrightness;
 
+        //If true, scales the brightness to half of desired.
+        public boolean lowPowerMode;
+
         // If true, prevents the screen from completely turning on if it is currently off.
         // The display does not enter a "ready" state if this flag is true and screen on is
         // blocked.  The window manager policy blocks screen on while it prepares the keyguard to
@@ -203,6 +206,7 @@
             screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment;
             useAutoBrightness = other.useAutoBrightness;
             blockScreenOn = other.blockScreenOn;
+            lowPowerMode = other.lowPowerMode;
         }
 
         @Override
@@ -218,7 +222,8 @@
                     && screenBrightness == other.screenBrightness
                     && screenAutoBrightnessAdjustment == other.screenAutoBrightnessAdjustment
                     && useAutoBrightness == other.useAutoBrightness
-                    && blockScreenOn == other.blockScreenOn;
+                    && blockScreenOn == other.blockScreenOn
+                    && lowPowerMode == other.lowPowerMode;
         }
 
         @Override
@@ -233,7 +238,8 @@
                     + ", screenBrightness=" + screenBrightness
                     + ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment
                     + ", useAutoBrightness=" + useAutoBrightness
-                    + ", blockScreenOn=" + blockScreenOn;
+                    + ", blockScreenOn=" + blockScreenOn
+                    + ", lowPowerMode=" + lowPowerMode;
         }
     }
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 8b7467f..4857533 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -21,6 +21,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Formatter;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -30,6 +31,8 @@
 import android.text.format.DateFormat;
 import android.util.Printer;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 import android.util.TimeUtils;
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
@@ -537,6 +540,7 @@
         public static final byte CMD_START = 4;
         public static final byte CMD_CURRENT_TIME = 5;
         public static final byte CMD_OVERFLOW = 6;
+        public static final byte CMD_RESET = 7;
 
         public byte cmd = CMD_NULL;
         
@@ -620,6 +624,8 @@
         public static final int EVENT_SYNC = 0x0004;
         // Number of event types.
         public static final int EVENT_COUNT = 0x0005;
+        // Mask to extract out only the type part of the event.
+        public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH);
 
         public static final int EVENT_PROC_START = EVENT_PROC | EVENT_FLAG_START;
         public static final int EVENT_PROC_FINISH = EVENT_PROC | EVENT_FLAG_FINISH;
@@ -684,7 +690,7 @@
                 dest.writeInt(eventCode);
                 eventTag.writeToParcel(dest, flags);
             }
-            if (cmd == CMD_CURRENT_TIME) {
+            if (cmd == CMD_CURRENT_TIME || cmd == CMD_RESET) {
                 dest.writeLong(currentTime);
             }
         }
@@ -722,7 +728,7 @@
                 eventCode = EVENT_NONE;
                 eventTag = null;
             }
-            if (cmd == CMD_CURRENT_TIME) {
+            if (cmd == CMD_CURRENT_TIME || cmd == CMD_RESET) {
                 currentTime = src.readLong();
             } else {
                 currentTime = 0;
@@ -833,7 +839,59 @@
             return true;
         }
     }
-    
+
+    public final static class HistoryEventTracker {
+        private final HashMap<String, SparseIntArray>[] mActiveEvents
+                = (HashMap<String, SparseIntArray>[]) new HashMap[HistoryItem.EVENT_COUNT];
+
+        public boolean updateState(int code, String name, int uid, int poolIdx) {
+            if ((code&HistoryItem.EVENT_FLAG_START) != 0) {
+                int idx = code&HistoryItem.EVENT_TYPE_MASK;
+                HashMap<String, SparseIntArray> active = mActiveEvents[idx];
+                if (active == null) {
+                    active = new HashMap<String, SparseIntArray>();
+                    mActiveEvents[idx] = active;
+                }
+                SparseIntArray uids = active.get(name);
+                if (uids == null) {
+                    uids = new SparseIntArray();
+                    active.put(name, uids);
+                }
+                if (uids.indexOfKey(uid) >= 0) {
+                    // Already set, nothing to do!
+                    return false;
+                }
+                uids.put(uid, poolIdx);
+            } else if ((code&HistoryItem.EVENT_FLAG_FINISH) != 0) {
+                int idx = code&HistoryItem.EVENT_TYPE_MASK;
+                HashMap<String, SparseIntArray> active = mActiveEvents[idx];
+                if (active == null) {
+                    // not currently active, nothing to do.
+                    return false;
+                }
+                SparseIntArray uids = active.get(name);
+                if (uids == null) {
+                    // not currently active, nothing to do.
+                    return false;
+                }
+                idx = uids.indexOfKey(uid);
+                if (idx < 0) {
+                    // not currently active, nothing to do.
+                    return false;
+                }
+                uids.removeAt(idx);
+                if (uids.size() <= 0) {
+                    active.remove(name);
+                }
+            }
+            return true;
+        }
+
+        public HashMap<String, SparseIntArray> getStateForEvent(int code) {
+            return mActiveEvents[code];
+        }
+    }
+
     public static final class BitDescription {
         public final int mask;
         public final int shift;
@@ -861,7 +919,7 @@
             this.shortValues = shortValues;
         }
     }
-    
+
     public abstract int getHistoryTotalSize();
 
     public abstract int getHistoryUsedSize();
@@ -2958,10 +3016,14 @@
                     pw.print(":");
                 }
                 pw.println("START");
-            } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
+            } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
+                    || rec.cmd == HistoryItem.CMD_RESET) {
                 if (checkin) {
                     pw.print(":");
                 }
+                if (rec.cmd == HistoryItem.CMD_RESET) {
+                    pw.print("RESET:");
+                }
                 pw.print("TIME:");
                 if (checkin) {
                     pw.println(rec.currentTime);
@@ -3187,6 +3249,86 @@
     public static final int DUMP_INCLUDE_HISTORY = 1<<3;
     public static final int DUMP_VERBOSE = 1<<4;
 
+    private void dumpHistoryLocked(PrintWriter pw, int flags, long histStart, boolean checkin) {
+        final HistoryPrinter hprinter = new HistoryPrinter();
+        final HistoryItem rec = new HistoryItem();
+        long lastTime = -1;
+        long baseTime = -1;
+        boolean printed = false;
+        HistoryEventTracker tracker = null;
+        while (getNextHistoryLocked(rec)) {
+            lastTime = rec.time;
+            if (baseTime < 0) {
+                baseTime = lastTime;
+            }
+            if (rec.time >= histStart) {
+                if (histStart >= 0 && !printed) {
+                    if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
+                            || rec.cmd == HistoryItem.CMD_RESET) {
+                        printed = true;
+                    } else if (rec.currentTime != 0) {
+                        printed = true;
+                        byte cmd = rec.cmd;
+                        rec.cmd = HistoryItem.CMD_CURRENT_TIME;
+                        if (checkin) {
+                            pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                            pw.print(HISTORY_DATA); pw.print(',');
+                        }
+                        hprinter.printNextItem(pw, rec, baseTime, checkin,
+                                (flags&DUMP_VERBOSE) != 0);
+                        rec.cmd = cmd;
+                    }
+                    if (tracker != null) {
+                        int oldCode = rec.eventCode;
+                        HistoryTag oldTag = rec.eventTag;
+                        rec.eventTag = new HistoryTag();
+                        for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
+                            HashMap<String, SparseIntArray> active
+                                    = tracker.getStateForEvent(i);
+                            if (active == null) {
+                                continue;
+                            }
+                            for (HashMap.Entry<String, SparseIntArray> ent
+                                    : active.entrySet()) {
+                                SparseIntArray uids = ent.getValue();
+                                for (int j=0; j<uids.size(); j++) {
+                                    rec.eventCode = i;
+                                    rec.eventTag.string = ent.getKey();
+                                    rec.eventTag.uid = uids.keyAt(j);
+                                    rec.eventTag.poolIdx = uids.valueAt(j);
+                                    if (checkin) {
+                                        pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                                        pw.print(HISTORY_DATA); pw.print(',');
+                                    }
+                                    hprinter.printNextItem(pw, rec, baseTime, checkin,
+                                            (flags&DUMP_VERBOSE) != 0);
+                                }
+                            }
+                        }
+                        rec.eventCode = oldCode;
+                        rec.eventTag = oldTag;
+                        tracker = null;
+                    }
+                }
+                if (checkin) {
+                    pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                    pw.print(HISTORY_DATA); pw.print(',');
+                }
+                hprinter.printNextItem(pw, rec, baseTime, checkin,
+                        (flags&DUMP_VERBOSE) != 0);
+            } else if (rec.eventCode != HistoryItem.EVENT_NONE) {
+                if (tracker == null) {
+                    tracker = new HistoryEventTracker();
+                }
+                tracker.updateState(rec.eventCode, rec.eventTag.string,
+                        rec.eventTag.uid, rec.eventTag.poolIdx);
+            }
+        }
+        if (histStart >= 0) {
+            pw.print(checkin ? "NEXT: " : "  NEXT: "); pw.println(lastTime+1);
+        }
+    }
+
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
      *
@@ -3200,9 +3342,6 @@
                 (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0;
 
         if ((flags&DUMP_HISTORY_ONLY) != 0 || !filtering) {
-            long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
-
-            final HistoryItem rec = new HistoryItem();
             final long historyTotalSize = getHistoryTotalSize();
             final long historyUsedSize = getHistoryUsedSize();
             if (startIteratingHistoryLocked()) {
@@ -3218,35 +3357,7 @@
                     pw.print(" strings using ");
                     printSizeValue(pw, getHistoryStringPoolBytes());
                     pw.println("):");
-                    HistoryPrinter hprinter = new HistoryPrinter();
-                    long lastTime = -1;
-                    long baseTime = -1;
-                    boolean printed = false;
-                    while (getNextHistoryLocked(rec)) {
-                        lastTime = rec.time;
-                        if (baseTime < 0) {
-                            baseTime = lastTime;
-                        }
-                        if (rec.time >= histStart) {
-                            if (histStart >= 0 && !printed) {
-                                if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
-                                    printed = true;
-                                } else if (rec.currentTime != 0) {
-                                    printed = true;
-                                    byte cmd = rec.cmd;
-                                    rec.cmd = HistoryItem.CMD_CURRENT_TIME;
-                                    hprinter.printNextItem(pw, rec, baseTime, false,
-                                            (flags&DUMP_VERBOSE) != 0);
-                                    rec.cmd = cmd;
-                                }
-                            }
-                            hprinter.printNextItem(pw, rec, baseTime, false,
-                                    (flags&DUMP_VERBOSE) != 0);
-                        }
-                    }
-                    if (histStart >= 0) {
-                        pw.print("  NEXT: "); pw.println(lastTime+1);
-                    }
+                    dumpHistoryLocked(pw, flags, histStart, false);
                     pw.println();
                 } finally {
                     finishIteratingHistoryLocked();
@@ -3255,6 +3366,7 @@
 
             if (startIteratingOldHistoryLocked()) {
                 try {
+                    final HistoryItem rec = new HistoryItem();
                     pw.println("Old battery History:");
                     HistoryPrinter hprinter = new HistoryPrinter();
                     long baseTime = -1;
@@ -3348,7 +3460,6 @@
                 (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0;
 
         if ((flags&DUMP_INCLUDE_HISTORY) != 0 || (flags&DUMP_HISTORY_ONLY) != 0) {
-            final HistoryItem rec = new HistoryItem();
             if (startIteratingHistoryLocked()) {
                 try {
                     for (int i=0; i<getHistoryStringPoolSize(); i++) {
@@ -3365,37 +3476,7 @@
                         pw.print("\"");
                         pw.println();
                     }
-                    HistoryPrinter hprinter = new HistoryPrinter();
-                    long lastTime = -1;
-                    long baseTime = -1;
-                    boolean printed = false;
-                    while (getNextHistoryLocked(rec)) {
-                        lastTime = rec.time;
-                        if (baseTime < 0) {
-                            baseTime = lastTime;
-                        }
-                        if (rec.time >= histStart) {
-                            if (histStart >= 0 && !printed) {
-                                if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
-                                    printed = true;
-                                } else if (rec.currentTime != 0) {
-                                    printed = true;
-                                    byte cmd = rec.cmd;
-                                    rec.cmd = HistoryItem.CMD_CURRENT_TIME;
-                                    pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
-                                    pw.print(HISTORY_DATA); pw.print(',');
-                                    hprinter.printNextItem(pw, rec, baseTime, true, false);
-                                    rec.cmd = cmd;
-                                }
-                            }
-                            pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
-                            pw.print(HISTORY_DATA); pw.print(',');
-                            hprinter.printNextItem(pw, rec, baseTime, true, false);
-                        }
-                    }
-                    if (histStart >= 0) {
-                        pw.print("NEXT: "); pw.println(lastTime+1);
-                    }
+                    dumpHistoryLocked(pw, flags, histStart, true);
                 } finally {
                     finishIteratingHistoryLocked();
                 }
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index a675821..be3b6ce 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -21,6 +21,8 @@
 import android.graphics.Paint;
 import android.util.SparseIntArray;
 
+import com.android.internal.util.VirtualRefBasePtr;
+
 import java.lang.ref.WeakReference;
 
 /**
@@ -70,28 +72,32 @@
     public static final int DELTA_TYPE_DELTA = 1;
 
     private RenderNode mTarget;
-    private long mNativePtr;
+    private VirtualRefBasePtr mNativePtr;
 
     public int mapViewPropertyToRenderProperty(int viewProperty) {
         return sViewPropertyAnimatorMap.get(viewProperty);
     }
 
     public RenderNodeAnimator(int property, int deltaType, float deltaValue) {
-        mNativePtr = nCreateAnimator(new WeakReference<RenderNodeAnimator>(this),
-                property, deltaType, deltaValue);
+        init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this),
+                property, deltaType, deltaValue));
     }
 
     public RenderNodeAnimator(CanvasProperty<Float> property, int deltaType, float deltaValue) {
-        mNativePtr = nCreateCanvasPropertyFloatAnimator(
+        init(nCreateCanvasPropertyFloatAnimator(
                 new WeakReference<RenderNodeAnimator>(this),
-                property.getNativeContainer(), deltaType, deltaValue);
+                property.getNativeContainer(), deltaType, deltaValue));
     }
 
     public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField,
             int deltaType, float deltaValue) {
-        mNativePtr = nCreateCanvasPropertyPaintAnimator(
+        init(nCreateCanvasPropertyPaintAnimator(
                 new WeakReference<RenderNodeAnimator>(this),
-                property.getNativeContainer(), paintField, deltaType, deltaValue);
+                property.getNativeContainer(), paintField, deltaType, deltaValue));
+    }
+
+    private void init(long ptr) {
+        mNativePtr = new VirtualRefBasePtr(ptr);
     }
 
     public void start(View target) {
@@ -115,11 +121,11 @@
     }
 
     public void setDuration(int duration) {
-        nSetDuration(mNativePtr, duration);
+        nSetDuration(mNativePtr.get(), duration);
     }
 
     long getNativeAnimator() {
-        return mNativePtr;
+        return mNativePtr.get();
     }
 
     private void onFinished() {
@@ -134,16 +140,6 @@
         }
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            nUnref(mNativePtr);
-            mNativePtr = 0;
-        } finally {
-            super.finalize();
-        }
-    }
-
     private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis,
             int property, int deltaValueType, float deltaValue);
     private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis,
@@ -151,5 +147,4 @@
     private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis,
             long canvasProperty, int paintField, int deltaValueType, float deltaValue);
     private static native void nSetDuration(long nativePtr, int duration);
-    private static native void nUnref(long nativePtr);
 }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index e1c6f52..be0c27d 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -49,7 +49,6 @@
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewRootImpl;
-import android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -322,8 +321,6 @@
      * The buffer to retrieve the view location in screen coordinates in {@link #updateCursor}.
      */
     private final int[] mViewTopLeft = new int[2];
-    private final CursorAnchorInfoBuilder mCursorAnchorInfoBuilder = new CursorAnchorInfoBuilder();
-
     // -----------------------------------------------------------
     
     /**
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 25bcd44..ac12357 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -50,28 +50,6 @@
     private static WebViewFactoryProvider sProviderInstance;
     private static final Object sProviderLock = new Object();
 
-    public static boolean isExperimentalWebViewAvailable() {
-        // TODO: Remove callers of this method then remove it.
-        return false;  // Hide the toggle in Developer Settings.
-    }
-
-    /** @hide */
-    public static void setUseExperimentalWebView(boolean enable) {
-        // TODO: Remove callers of this method then remove it.
-    }
-
-    /** @hide */
-    public static boolean useExperimentalWebView() {
-        // TODO: Remove callers of this method then remove it.
-        return true;
-    }
-
-    /** @hide */
-    public static boolean isUseExperimentalWebViewSet() {
-        // TODO: Remove callers of this method then remove it.
-        return false;  // User has not modifed Developer Settings
-    }
-
     static WebViewFactoryProvider getProvider() {
         synchronized (sProviderLock) {
             // For now the main purpose of this function (and the factory abstraction) is to keep
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index f4cd5fc..565ea13 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3267,7 +3267,7 @@
         }
     }
 
-    private boolean startScrollIfNeeded(int y, MotionEvent vtev) {
+    private boolean startScrollIfNeeded(int x, int y, MotionEvent vtev) {
         // Check if we have moved far enough that it looks more like a
         // scroll than a tap
         final int deltaY = y - mMotionY;
@@ -3296,14 +3296,14 @@
             if (parent != null) {
                 parent.requestDisallowInterceptTouchEvent(true);
             }
-            scrollIfNeeded(y, vtev);
+            scrollIfNeeded(x, y, vtev);
             return true;
         }
 
         return false;
     }
 
-    private void scrollIfNeeded(int y, MotionEvent vtev) {
+    private void scrollIfNeeded(int x, int y, MotionEvent vtev) {
         int rawDeltaY = y - mMotionY;
         if (dispatchNestedPreScroll(0, rawDeltaY, mScrollConsumed, mScrollOffset)) {
             rawDeltaY -= mScrollConsumed[1];
@@ -3384,33 +3384,39 @@
                                 vtev.offsetLocation(0, mScrollOffset[1]);
                             }
                         } else {
-                            overScrollBy(0, overscroll, 0, mScrollY, 0, 0,
-                                    0, mOverscrollDistance, true);
-                            if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
-                                // Don't allow overfling if we're at the edge.
-                                if (mVelocityTracker != null) {
-                                    mVelocityTracker.clear();
-                                }
+                            final boolean atOverscrollEdge = overScrollBy(0, overscroll,
+                                    0, mScrollY, 0, 0, 0, mOverscrollDistance, true);
+
+                            if (atOverscrollEdge && mVelocityTracker != null) {
+                                // Don't allow overfling if we're at the edge
+                                mVelocityTracker.clear();
                             }
 
                             final int overscrollMode = getOverScrollMode();
                             if (overscrollMode == OVER_SCROLL_ALWAYS ||
                                     (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
                                             !contentFits())) {
-                                mDirection = 0; // Reset when entering overscroll.
-                                mTouchMode = TOUCH_MODE_OVERSCROLL;
-                                if (deltaY > 0) {
-                                    mEdgeGlowTop.onPull((float) overscroll / getHeight());
+                                if (!atOverscrollEdge) {
+                                    mDirection = 0; // Reset when entering overscroll.
+                                    mTouchMode = TOUCH_MODE_OVERSCROLL;
+                                }
+                                if (incrementalDeltaY > 0) {
+                                    mEdgeGlowTop.onPull((float) overscroll / getHeight(),
+                                            (float) x / getWidth());
                                     if (!mEdgeGlowBottom.isFinished()) {
                                         mEdgeGlowBottom.onRelease();
                                     }
-                                    invalidate(mEdgeGlowTop.getBounds(false));
-                                } else if (deltaY < 0) {
-                                    mEdgeGlowBottom.onPull((float) overscroll / getHeight());
+                                    invalidate(0, 0, getWidth(),
+                                            mEdgeGlowTop.getMaxHeight() + getPaddingTop());
+                                } else if (incrementalDeltaY < 0) {
+                                    mEdgeGlowBottom.onPull((float) overscroll / getHeight(),
+                                            1.f - (float) x / getWidth());
                                     if (!mEdgeGlowTop.isFinished()) {
                                         mEdgeGlowTop.onRelease();
                                     }
-                                    invalidate(mEdgeGlowBottom.getBounds(true));
+                                    invalidate(0, getHeight() - getPaddingBottom() -
+                                            mEdgeGlowBottom.getMaxHeight(), getWidth(),
+                                            getHeight());
                                 }
                             }
                         }
@@ -3445,17 +3451,22 @@
                             (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
                                     !contentFits())) {
                         if (rawDeltaY > 0) {
-                            mEdgeGlowTop.onPull((float) overScrollDistance / getHeight());
+                            mEdgeGlowTop.onPull((float) overScrollDistance / getHeight(),
+                                    (float) x / getWidth());
                             if (!mEdgeGlowBottom.isFinished()) {
                                 mEdgeGlowBottom.onRelease();
                             }
-                            invalidate(mEdgeGlowTop.getBounds(false));
+                            invalidate(0, 0, getWidth(),
+                                    mEdgeGlowTop.getMaxHeight() + getPaddingTop());
                         } else if (rawDeltaY < 0) {
-                            mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight());
+                            mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight(),
+                                    1.f - (float) x / getWidth());
                             if (!mEdgeGlowTop.isFinished()) {
                                 mEdgeGlowTop.onRelease();
                             }
-                            invalidate(mEdgeGlowBottom.getBounds(true));
+                            invalidate(0, getHeight() - getPaddingBottom() -
+                                    mEdgeGlowBottom.getMaxHeight(), getWidth(),
+                                    getHeight());
                         }
                     }
                 }
@@ -3703,7 +3714,7 @@
             case TOUCH_MODE_DONE_WAITING:
                 // Check if we have moved far enough that it looks more like a
                 // scroll than a tap. If so, we'll enter scrolling mode.
-                if (startScrollIfNeeded(y, vtev)) {
+                if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, vtev)) {
                     break;
                 }
                 // Otherwise, check containment within list bounds. If we're
@@ -3723,7 +3734,7 @@
                 break;
             case TOUCH_MODE_SCROLL:
             case TOUCH_MODE_OVERSCROLL:
-                scrollIfNeeded(y, vtev);
+                scrollIfNeeded((int) ev.getX(pointerIndex), y, vtev);
                 break;
         }
     }
@@ -4022,8 +4033,8 @@
                 canvas.translate(leftPadding, edgeY);
                 mEdgeGlowTop.setSize(width, getHeight());
                 if (mEdgeGlowTop.draw(canvas)) {
-                    mEdgeGlowTop.setPosition(leftPadding, edgeY);
-                    invalidate(mEdgeGlowTop.getBounds(false));
+                    invalidate(0, 0, getWidth(),
+                            mEdgeGlowTop.getMaxHeight() + getPaddingTop());
                 }
                 canvas.restoreToCount(restoreCount);
             }
@@ -4040,9 +4051,9 @@
                 canvas.rotate(180, width, 0);
                 mEdgeGlowBottom.setSize(width, height);
                 if (mEdgeGlowBottom.draw(canvas)) {
-                    // Account for the rotation
-                    mEdgeGlowBottom.setPosition(edgeX + width, edgeY);
-                    invalidate(mEdgeGlowBottom.getBounds(true));
+                    invalidate(0, getHeight() - getPaddingBottom() -
+                            mEdgeGlowBottom.getMaxHeight(), getWidth(),
+                            getHeight());
                 }
                 canvas.restoreToCount(restoreCount);
             }
@@ -4161,7 +4172,7 @@
                 final int y = (int) ev.getY(pointerIndex);
                 initVelocityTrackerIfNotExists();
                 mVelocityTracker.addMovement(ev);
-                if (startScrollIfNeeded(y, null)) {
+                if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) {
                     return true;
                 }
                 break;
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index fa37443..83fbe8f 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -16,7 +16,14 @@
 
 package android.widget;
 
+import android.content.res.TypedArray;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Xfermode;
+import android.util.Log;
 import com.android.internal.R;
 
 import android.content.Context;
@@ -59,12 +66,10 @@
     private static final int PULL_DECAY_TIME = 1000;
 
     private static final float MAX_ALPHA = 1.f;
-    private static final float HELD_EDGE_SCALE_Y = 0.5f;
 
-    private static final float MAX_GLOW_HEIGHT = 4.f;
+    private static final float MAX_GLOW_HEIGHT = 1.5f;
 
-    private static final float PULL_GLOW_BEGIN = 1.f;
-    private static final float PULL_EDGE_BEGIN = 0.6f;
+    private static final float PULL_GLOW_BEGIN = 0.f;
 
     // Minimum velocity that will be absorbed
     private static final int MIN_VELOCITY = 100;
@@ -73,24 +78,11 @@
 
     private static final float EPSILON = 0.001f;
 
-    private final Drawable mEdge;
-    private final Drawable mGlow;
-    private int mWidth;
-    private int mHeight;
-    private int mX;
-    private int mY;
-    private static final int MIN_WIDTH = 300;
-    private final int mMinWidth;
+    private static final float SIN_45 = (float) Math.sin(Math.PI / 4);
 
-    private float mEdgeAlpha;
-    private float mEdgeScaleY;
     private float mGlowAlpha;
     private float mGlowScaleY;
 
-    private float mEdgeAlphaStart;
-    private float mEdgeAlphaFinish;
-    private float mEdgeScaleYStart;
-    private float mEdgeScaleYFinish;
     private float mGlowAlphaStart;
     private float mGlowAlphaFinish;
     private float mGlowScaleYStart;
@@ -107,16 +99,11 @@
     private static final int STATE_RECEDE = 3;
     private static final int STATE_PULL_DECAY = 4;
 
-    // How much dragging should effect the height of the edge image.
-    // Number determined by user testing.
-    private static final int PULL_DISTANCE_EDGE_FACTOR = 7;
-
     // How much dragging should effect the height of the glow image.
     // Number determined by user testing.
     private static final int PULL_DISTANCE_GLOW_FACTOR = 7;
     private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 1.1f;
 
-    private static final int VELOCITY_EDGE_FACTOR = 8;
     private static final int VELOCITY_GLOW_FACTOR = 12;
 
     private int mState = STATE_IDLE;
@@ -124,30 +111,26 @@
     private float mPullDistance;
     
     private final Rect mBounds = new Rect();
-
-    private final int mEdgeHeight;
-    private final int mGlowHeight;
-    private final int mGlowWidth;
-    private final int mMaxEffectHeight;
+    private final RectF mArcRect = new RectF();
+    private final Paint mPaint = new Paint();
+    private float mRadius;
+    private float mDisplacement = 0.5f;
+    private float mTargetDisplacement = 0.5f;
 
     /**
      * Construct a new EdgeEffect with a theme appropriate for the provided context.
      * @param context Context used to provide theming and resource information for the EdgeEffect
      */
     public EdgeEffect(Context context) {
-        final Resources res = context.getResources();
-        mEdge = context.getDrawable(R.drawable.overscroll_edge);
-        mGlow = context.getDrawable(R.drawable.overscroll_glow);
-
-        mEdgeHeight = mEdge.getIntrinsicHeight();
-        mGlowHeight = mGlow.getIntrinsicHeight();
-        mGlowWidth = mGlow.getIntrinsicWidth();
-
-        mMaxEffectHeight = (int) (Math.min(
-                mGlowHeight * MAX_GLOW_HEIGHT * mGlowHeight / mGlowWidth * 0.6f,
-                mGlowHeight * MAX_GLOW_HEIGHT) + 0.5f);
-
-        mMinWidth = (int) (res.getDisplayMetrics().density * MIN_WIDTH + 0.5f);
+        mPaint.setAntiAlias(true);
+        final TypedArray a = context.obtainStyledAttributes(
+                com.android.internal.R.styleable.EdgeEffect);
+        final int themeColor = a.getColor(
+                com.android.internal.R.styleable.EdgeEffect_colorPrimaryLight, 0xff666666);
+        a.recycle();
+        mPaint.setColor((themeColor & 0xffffff) | 0x66000000);
+        mPaint.setStyle(Paint.Style.FILL);
+        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
         mInterpolator = new DecelerateInterpolator();
     }
 
@@ -158,20 +141,12 @@
      * @param height Effect height in pixels
      */
     public void setSize(int width, int height) {
-        mWidth = width;
-        mHeight = height;
-    }
+        final float r = width * 0.5f / SIN_45;
+        final float y = SIN_45 * r;
+        final float h = r - y;
+        mRadius = r;
 
-    /**
-     * Set the position of this edge effect in pixels. This position is
-     * only used by {@link #getBounds(boolean)}.
-     * 
-     * @param x The position of the edge effect on the X axis
-     * @param y The position of the edge effect on the Y axis
-     */
-    void setPosition(int x, int y) {
-        mX = x;
-        mY = y;
+        mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h));
     }
 
     /**
@@ -199,17 +174,38 @@
      * The host view should always {@link android.view.View#invalidate()} after this
      * and draw the results accordingly.
      *
+     * <p>Views using EdgeEffect should favor {@link #onPull(float, float)} when the displacement
+     * of the pull point is known.</p>
+     *
      * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
      *                      1.f (full length of the view) or negative values to express change
      *                      back toward the edge reached to initiate the effect.
      */
     public void onPull(float deltaDistance) {
+        onPull(deltaDistance, 0.5f);
+    }
+
+    /**
+     * A view should call this when content is pulled away from an edge by the user.
+     * This will update the state of the current visual effect and its associated animation.
+     * The host view should always {@link android.view.View#invalidate()} after this
+     * and draw the results accordingly.
+     *
+     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
+     *                      1.f (full length of the view) or negative values to express change
+     *                      back toward the edge reached to initiate the effect.
+     * @param displacement The displacement from the starting side of the effect of the point
+     *                     initiating the pull. In the case of touch this is the finger position.
+     *                     Values may be from 0-1.
+     */
+    public void onPull(float deltaDistance, float displacement) {
         final long now = AnimationUtils.currentAnimationTimeMillis();
+        mTargetDisplacement = displacement;
         if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) {
             return;
         }
         if (mState != STATE_PULL) {
-            mGlowScaleY = PULL_GLOW_BEGIN;
+            mGlowScaleY = Math.max(PULL_GLOW_BEGIN, mGlowScaleY);
         }
         mState = STATE_PULL;
 
@@ -217,15 +213,10 @@
         mDuration = PULL_TIME;
 
         mPullDistance += deltaDistance;
-        float distance = Math.abs(mPullDistance);
-
-        mEdgeAlpha = mEdgeAlphaStart = Math.max(PULL_EDGE_BEGIN, Math.min(distance, MAX_ALPHA));
-        mEdgeScaleY = mEdgeScaleYStart = Math.max(
-                HELD_EDGE_SCALE_Y, Math.min(distance * PULL_DISTANCE_EDGE_FACTOR, 1.f));
 
         mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
                 mGlowAlpha +
-                (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
+                        (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
 
         float glowChange = Math.abs(deltaDistance);
         if (deltaDistance > 0 && mPullDistance < 0) {
@@ -239,8 +230,6 @@
         mGlowScaleY = mGlowScaleYStart = Math.min(MAX_GLOW_HEIGHT, Math.max(
                 0, mGlowScaleY + glowChange * PULL_DISTANCE_GLOW_FACTOR));
 
-        mEdgeAlphaFinish = mEdgeAlpha;
-        mEdgeScaleYFinish = mEdgeScaleY;
         mGlowAlphaFinish = mGlowAlpha;
         mGlowScaleYFinish = mGlowScaleY;
     }
@@ -259,13 +248,9 @@
         }
 
         mState = STATE_RECEDE;
-        mEdgeAlphaStart = mEdgeAlpha;
-        mEdgeScaleYStart = mEdgeScaleY;
         mGlowAlphaStart = mGlowAlpha;
         mGlowScaleYStart = mGlowScaleY;
 
-        mEdgeAlphaFinish = 0.f;
-        mEdgeScaleYFinish = 0.f;
         mGlowAlphaFinish = 0.f;
         mGlowScaleYFinish = 0.f;
 
@@ -290,30 +275,21 @@
         mStartTime = AnimationUtils.currentAnimationTimeMillis();
         mDuration = 0.15f + (velocity * 0.02f);
 
-        // The edge should always be at least partially visible, regardless
-        // of velocity.
-        mEdgeAlphaStart = 0.f;
-        mEdgeScaleY = mEdgeScaleYStart = 0.f;
         // The glow depends more on the velocity, and therefore starts out
         // nearly invisible.
         mGlowAlphaStart = 0.3f;
-        mGlowScaleYStart = 0.f;
+        mGlowScaleYStart = Math.max(mGlowScaleY, 0.f);
 
-        // Factor the velocity by 8. Testing on device shows this works best to
-        // reflect the strength of the user's scrolling.
-        mEdgeAlphaFinish = Math.max(0, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1));
-        // Edge should never get larger than the size of its asset.
-        mEdgeScaleYFinish = Math.max(
-                HELD_EDGE_SCALE_Y, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1.f));
 
         // Growth for the size of the glow should be quadratic to properly
         // respond
         // to a user's scrolling speed. The faster the scrolling speed, the more
         // intense the effect should be for both the size and the saturation.
-        mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
+        mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2, 1.f);
         // Alpha should change for the glow as well as size.
         mGlowAlphaFinish = Math.max(
                 mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
+        mTargetDisplacement = 0.5f;
     }
 
 
@@ -330,52 +306,42 @@
     public boolean draw(Canvas canvas) {
         update();
 
-        mGlow.setAlpha((int) (Math.max(0, Math.min(mGlowAlpha, 1)) * 255));
+        final int count = canvas.save();
 
-        int glowBottom = (int) Math.min(
-                mGlowHeight * mGlowScaleY * mGlowHeight / mGlowWidth * 0.6f,
-                mGlowHeight * MAX_GLOW_HEIGHT);
-        if (mWidth < mMinWidth) {
-            // Center the glow and clip it.
-            int glowLeft = (mWidth - mMinWidth)/2;
-            mGlow.setBounds(glowLeft, 0, mWidth - glowLeft, glowBottom);
-        } else {
-            // Stretch the glow to fit.
-            mGlow.setBounds(0, 0, mWidth, glowBottom);
+        final float y = mBounds.height();
+        final float centerY = y - mRadius;
+        final float centerX = mBounds.centerX();
+        mArcRect.set(centerX - mRadius, centerY - mRadius, centerX + mRadius, centerY + mRadius);
+        canvas.scale(1.f, Math.min(mGlowScaleY, 1.f), centerX, 0);
+
+        final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f;
+        float translateX = mBounds.width() * displacement;
+        float translateY = 0;
+        if (mGlowScaleY > 1.f) {
+            translateY = (mGlowScaleY - 1.f) * mBounds.height();
         }
+        canvas.clipRect(Float.MIN_VALUE, mBounds.top,
+                Float.MAX_VALUE, Float.MAX_VALUE);
+        canvas.translate(translateX, translateY);
+        canvas.drawArc(mArcRect, 0, 180, true, mPaint);
+        canvas.restoreToCount(count);
 
-        mGlow.draw(canvas);
-
-        mEdge.setAlpha((int) (Math.max(0, Math.min(mEdgeAlpha, 1)) * 255));
-
-        int edgeBottom = (int) (mEdgeHeight * mEdgeScaleY);
-        if (mWidth < mMinWidth) {
-            // Center the edge and clip it.
-            int edgeLeft = (mWidth - mMinWidth)/2;
-            mEdge.setBounds(edgeLeft, 0, mWidth - edgeLeft, edgeBottom);
-        } else {
-            // Stretch the edge to fit.
-            mEdge.setBounds(0, 0, mWidth, edgeBottom);
-        }
-        mEdge.draw(canvas);
-
-        if (mState == STATE_RECEDE && glowBottom == 0 && edgeBottom == 0) {
+        boolean oneLastFrame = false;
+        if (mState == STATE_RECEDE && mGlowScaleY == 0) {
             mState = STATE_IDLE;
+            oneLastFrame = true;
         }
 
-        return mState != STATE_IDLE;
+        return mState != STATE_IDLE || oneLastFrame;
     }
 
     /**
-     * Returns the bounds of the edge effect.
-     * 
-     * @hide
+     * Return the maximum height that the edge effect will be drawn at given the original
+     * {@link #setSize(int, int) input size}.
+     * @return The maximum height of the edge effect
      */
-    public Rect getBounds(boolean reverse) {
-        mBounds.set(0, 0, mWidth, mMaxEffectHeight);
-        mBounds.offset(mX, mY - (reverse ? mMaxEffectHeight : 0));
-
-        return mBounds;
+    public int getMaxHeight() {
+        return (int) (mBounds.height() * MAX_GLOW_HEIGHT + 0.5f);
     }
 
     private void update() {
@@ -384,10 +350,9 @@
 
         final float interp = mInterpolator.getInterpolation(t);
 
-        mEdgeAlpha = mEdgeAlphaStart + (mEdgeAlphaFinish - mEdgeAlphaStart) * interp;
-        mEdgeScaleY = mEdgeScaleYStart + (mEdgeScaleYFinish - mEdgeScaleYStart) * interp;
         mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
         mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
+        mDisplacement = (mDisplacement + mTargetDisplacement) / 2;
 
         if (t >= 1.f - EPSILON) {
             switch (mState) {
@@ -396,14 +361,10 @@
                     mStartTime = AnimationUtils.currentAnimationTimeMillis();
                     mDuration = RECEDE_TIME;
 
-                    mEdgeAlphaStart = mEdgeAlpha;
-                    mEdgeScaleYStart = mEdgeScaleY;
                     mGlowAlphaStart = mGlowAlpha;
                     mGlowScaleYStart = mGlowScaleY;
 
-                    // After absorb, the glow and edge should fade to nothing.
-                    mEdgeAlphaFinish = 0.f;
-                    mEdgeScaleYFinish = 0.f;
+                    // After absorb, the glow should fade to nothing.
                     mGlowAlphaFinish = 0.f;
                     mGlowScaleYFinish = 0.f;
                     break;
@@ -412,26 +373,14 @@
                     mStartTime = AnimationUtils.currentAnimationTimeMillis();
                     mDuration = PULL_DECAY_TIME;
 
-                    mEdgeAlphaStart = mEdgeAlpha;
-                    mEdgeScaleYStart = mEdgeScaleY;
                     mGlowAlphaStart = mGlowAlpha;
                     mGlowScaleYStart = mGlowScaleY;
 
-                    // After pull, the glow and edge should fade to nothing.
-                    mEdgeAlphaFinish = 0.f;
-                    mEdgeScaleYFinish = 0.f;
+                    // After pull, the glow should fade to nothing.
                     mGlowAlphaFinish = 0.f;
                     mGlowScaleYFinish = 0.f;
                     break;
                 case STATE_PULL_DECAY:
-                    // When receding, we want edge to decrease more slowly
-                    // than the glow.
-                    float factor = mGlowScaleYFinish != 0 ? 1
-                            / (mGlowScaleYFinish * mGlowScaleYFinish)
-                            : Float.MAX_VALUE;
-                    mEdgeScaleY = mEdgeScaleYStart +
-                        (mEdgeScaleYFinish - mEdgeScaleYStart) *
-                            interp * factor;
                     mState = STATE_RECEDE;
                     break;
                 case STATE_RECEDE:
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 25d4f42..0c65c50 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -616,12 +616,14 @@
                     if (canOverscroll) {
                         final int pulledToX = oldX + deltaX;
                         if (pulledToX < 0) {
-                            mEdgeGlowLeft.onPull((float) deltaX / getWidth());
+                            mEdgeGlowLeft.onPull((float) deltaX / getWidth(),
+                                    1.f - ev.getY(activePointerIndex) / getHeight());
                             if (!mEdgeGlowRight.isFinished()) {
                                 mEdgeGlowRight.onRelease();
                             }
                         } else if (pulledToX > range) {
-                            mEdgeGlowRight.onPull((float) deltaX / getWidth());
+                            mEdgeGlowRight.onPull((float) deltaX / getWidth(),
+                                    ev.getY(activePointerIndex) / getHeight());
                             if (!mEdgeGlowLeft.isFinished()) {
                                 mEdgeGlowLeft.onRelease();
                             }
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 0fa75a6..fd04890 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -669,12 +669,14 @@
                     } else if (canOverscroll) {
                         final int pulledToY = oldY + deltaY;
                         if (pulledToY < 0) {
-                            mEdgeGlowTop.onPull((float) deltaY / getHeight());
+                            mEdgeGlowTop.onPull((float) deltaY / getHeight(),
+                                    ev.getX(activePointerIndex) / getWidth());
                             if (!mEdgeGlowBottom.isFinished()) {
                                 mEdgeGlowBottom.onRelease();
                             }
                         } else if (pulledToY > range) {
-                            mEdgeGlowBottom.onPull((float) deltaY / getHeight());
+                            mEdgeGlowBottom.onPull((float) deltaY / getHeight(),
+                                    1.f - ev.getX(activePointerIndex) / getWidth());
                             if (!mEdgeGlowTop.isFinished()) {
                                 mEdgeGlowTop.onRelease();
                             }
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index 882bec9..41f3337 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -1108,13 +1108,6 @@
                 mRuntime = runtime;
             }
         }
-        String webview = WebViewFactory.useExperimentalWebView() ? "chromeview" : "webview";
-        if (!Objects.equals(webview, mWebView)) {
-            changed = true;
-            if (update) {
-                mWebView = webview;
-            }
-        }
         return changed;
     }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 1aff190..7bd5b12 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -188,8 +188,7 @@
 
     boolean mShuttingDown;
 
-    HashMap<String, SparseBooleanArray>[] mActiveEvents
-            = (HashMap<String, SparseBooleanArray>[]) new HashMap[HistoryItem.EVENT_COUNT];
+    final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
 
     long mHistoryBaseTime;
     boolean mHaveBatteryLevel = false;
@@ -2297,44 +2296,8 @@
 
     public void noteEventLocked(int code, String name, int uid) {
         uid = mapUid(uid);
-        if ((code&HistoryItem.EVENT_FLAG_START) != 0) {
-            int idx = code&~HistoryItem.EVENT_FLAG_START;
-            HashMap<String, SparseBooleanArray> active = mActiveEvents[idx];
-            if (active == null) {
-                active = new HashMap<String, SparseBooleanArray>();
-                mActiveEvents[idx] = active;
-            }
-            SparseBooleanArray uids = active.get(name);
-            if (uids == null) {
-                uids = new SparseBooleanArray();
-                active.put(name, uids);
-            }
-            if (uids.get(uid)) {
-                // Already set, nothing to do!
-                return;
-            }
-            uids.put(uid, true);
-        } else if ((code&HistoryItem.EVENT_FLAG_FINISH) != 0) {
-            int idx = code&~HistoryItem.EVENT_FLAG_FINISH;
-            HashMap<String, SparseBooleanArray> active = mActiveEvents[idx];
-            if (active == null) {
-                // not currently active, nothing to do.
-                return;
-            }
-            SparseBooleanArray uids = active.get(name);
-            if (uids == null) {
-                // not currently active, nothing to do.
-                return;
-            }
-            idx = uids.indexOfKey(uid);
-            if (idx < 0 || !uids.valueAt(idx)) {
-                // not currently active, nothing to do.
-                return;
-            }
-            uids.removeAt(idx);
-            if (uids.size() <= 0) {
-                active.remove(name);
-            }
+        if (!mActiveEvents.updateState(code, name, uid, 0)) {
+            return;
         }
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
@@ -2348,6 +2311,9 @@
         }
     }
 
+    private String mInitialAcquireWakeName;
+    private int mInitialAcquireWakeUid = -1;
+
     public void noteStartWakeLocked(int uid, int pid, String name, String historyName, int type,
             boolean unimportantForLogging, long elapsedRealtime, long uptime) {
         uid = mapUid(uid);
@@ -2360,8 +2326,9 @@
                 if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
                         + Integer.toHexString(mHistoryCur.states));
                 mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
-                mHistoryCur.wakelockTag.string = historyName != null ? historyName : name;
-                mHistoryCur.wakelockTag.uid = uid;
+                mHistoryCur.wakelockTag.string = mInitialAcquireWakeName
+                        = historyName != null ? historyName : name;
+                mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
                 mWakeLockImportant = !unimportantForLogging;
                 addHistoryRecordLocked(elapsedRealtime, uptime);
             } else if (!mWakeLockImportant && !unimportantForLogging) {
@@ -2369,8 +2336,9 @@
                     // We'll try to update the last tag.
                     mHistoryLastWritten.wakelockTag = null;
                     mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
-                    mHistoryCur.wakelockTag.string = historyName != null ? historyName : name;
-                    mHistoryCur.wakelockTag.uid = uid;
+                    mHistoryCur.wakelockTag.string = mInitialAcquireWakeName
+                            = historyName != null ? historyName : name;
+                    mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
                     addHistoryRecordLocked(elapsedRealtime, uptime);
                 }
                 mWakeLockImportant = true;
@@ -2395,6 +2363,14 @@
                 mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
                         + Integer.toHexString(mHistoryCur.states));
+                if ((name != null && !name.equals(mInitialAcquireWakeName))
+                        || uid != mInitialAcquireWakeUid) {
+                    mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
+                    mHistoryCur.wakelockTag.string = name;
+                    mHistoryCur.wakelockTag.uid = uid;
+                }
+                mInitialAcquireWakeName = null;
+                mInitialAcquireWakeUid = -1;
                 addHistoryRecordLocked(elapsedRealtime, uptime);
             }
         }
@@ -5699,7 +5675,8 @@
         final long lastRealtime = out.time;
         final long lastWalltime = out.currentTime;
         readHistoryDelta(mHistoryBuffer, out);
-        if (out.cmd != HistoryItem.CMD_CURRENT_TIME && lastWalltime != 0) {
+        if (out.cmd != HistoryItem.CMD_CURRENT_TIME
+                && out.cmd != HistoryItem.CMD_RESET && lastWalltime != 0) {
             out.currentTime = lastWalltime + (out.time - lastRealtime);
         }
         return true;
@@ -5845,17 +5822,15 @@
 
     private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) {
         for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
-            HashMap<String, SparseBooleanArray> active = mActiveEvents[i];
+            HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(i);
             if (active == null) {
                 continue;
             }
-            for (HashMap.Entry<String, SparseBooleanArray> ent : active.entrySet()) {
-                SparseBooleanArray uids = ent.getValue();
+            for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
+                SparseIntArray uids = ent.getValue();
                 for (int j=0; j<uids.size(); j++) {
-                    if (uids.valueAt(j)) {
-                        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
-                                uids.keyAt(j));
-                    }
+                    addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
+                            uids.keyAt(j));
                 }
             }
         }
@@ -5971,7 +5946,8 @@
             boolean reset) {
         mRecordingHistory = true;
         mHistoryCur.currentTime = System.currentTimeMillis();
-        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME,
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs,
+                reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME,
                 mHistoryCur);
         mHistoryCur.currentTime = 0;
         if (reset) {
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 52281d9..34f62ba 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -450,6 +450,7 @@
     public void disconnect() {
         if ((mConnection != null) && (mSrcContext != null)) {
             mSrcContext.unbindService(mConnection);
+            mConnection = null;
         }
         try {
             // Send the DISCONNECTED, although it may not be received
@@ -463,10 +464,12 @@
         // Tell source we're disconnected.
         if (mSrcHandler != null) {
             replyDisconnected(STATUS_SUCCESSFUL);
+            mSrcHandler = null;
         }
         // Unlink only when bindService isn't used
         if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) {
             mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0);
+            mDeathMonitor = null;
         }
     }
 
diff --git a/core/java/com/android/internal/util/VirtualRefBasePtr.java b/core/java/com/android/internal/util/VirtualRefBasePtr.java
new file mode 100644
index 0000000..0bd4d3a
--- /dev/null
+++ b/core/java/com/android/internal/util/VirtualRefBasePtr.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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;
+
+/**
+ * Helper class that contains a strong reference to a VirtualRefBase native
+ * object. This will incStrong in the ctor, and decStrong in the finalizer
+ */
+public final class VirtualRefBasePtr {
+    private long mNativePtr;
+
+    public VirtualRefBasePtr(long ptr) {
+        mNativePtr = ptr;
+        nIncStrong(mNativePtr);
+    }
+
+    public long get() {
+        return mNativePtr;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            nDecStrong(mNativePtr);
+            mNativePtr = 0;
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private static native void nIncStrong(long ptr);
+    private static native void nDecStrong(long ptr);
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 51e2871..26f77b5 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -155,7 +155,8 @@
 	android_content_res_Configuration.cpp \
 	android_animation_PropertyValuesHolder.cpp \
 	com_android_internal_net_NetworkStatsFactory.cpp \
-	com_android_internal_os_Zygote.cpp
+	com_android_internal_os_Zygote.cpp \
+	com_android_internal_util_VirtualRefBasePtr.cpp
 
 LOCAL_C_INCLUDES += \
 	$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 66fbb8e..9941cd9 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -181,6 +181,7 @@
 extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
 extern int register_com_android_internal_net_NetworkStatsFactory(JNIEnv *env);
 extern int register_com_android_internal_os_Zygote(JNIEnv *env);
+extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
 
 static AndroidRuntime* gCurRuntime = NULL;
 
@@ -1262,6 +1263,7 @@
     REG_JNI(register_android_os_MemoryFile),
     REG_JNI(register_com_android_internal_os_ZygoteInit),
     REG_JNI(register_com_android_internal_os_Zygote),
+    REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
     REG_JNI(register_android_hardware_Camera),
     REG_JNI(register_android_hardware_camera2_CameraMetadata),
     REG_JNI(register_android_hardware_SensorManager),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index e0fa9ba4..0328517 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1,838 +1,838 @@
-#include "SkBitmap.h"

-#include "SkPixelRef.h"

-#include "SkImageEncoder.h"

-#include "SkColorPriv.h"

-#include "GraphicsJNI.h"

-#include "SkDither.h"

-#include "SkUnPreMultiply.h"

-#include "SkStream.h"

-

-#include <binder/Parcel.h>

-#include "android_os_Parcel.h"

-#include "android_util_Binder.h"

-#include "android_nio_utils.h"

-#include "CreateJavaOutputStreamAdaptor.h"

-

-#include <jni.h>

-

-#include <Caches.h>

-

-#if 0

-    #define TRACE_BITMAP(code)  code

-#else

-    #define TRACE_BITMAP(code)

-#endif

-

-///////////////////////////////////////////////////////////////////////////////

-// Conversions to/from SkColor, for get/setPixels, and the create method, which

-// is basically like setPixels

-

-typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,

-                              int x, int y);

-

-static void FromColor_D32(void* dst, const SkColor src[], int width,

-                          int, int) {

-    SkPMColor* d = (SkPMColor*)dst;

-

-    for (int i = 0; i < width; i++) {

-        *d++ = SkPreMultiplyColor(*src++);

-    }

-}

-

-static void FromColor_D32_Raw(void* dst, const SkColor src[], int width,

-                          int, int) {

-    // SkColor's ordering may be different from SkPMColor

-    if (SK_COLOR_MATCHES_PMCOLOR_BYTE_ORDER) {

-        memcpy(dst, src, width * sizeof(SkColor));

-        return;

-    }

-

-    // order isn't same, repack each pixel manually

-    SkPMColor* d = (SkPMColor*)dst;

-    for (int i = 0; i < width; i++) {

-        SkColor c = *src++;

-        *d++ = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),

-                                   SkColorGetG(c), SkColorGetB(c));

-    }

-}

-

-static void FromColor_D565(void* dst, const SkColor src[], int width,

-                           int x, int y) {

-    uint16_t* d = (uint16_t*)dst;

-

-    DITHER_565_SCAN(y);

-    for (int stop = x + width; x < stop; x++) {

-        SkColor c = *src++;

-        *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c),

-                                DITHER_VALUE(x));

-    }

-}

-

-static void FromColor_D4444(void* dst, const SkColor src[], int width,

-                            int x, int y) {

-    SkPMColor16* d = (SkPMColor16*)dst;

-

-    DITHER_4444_SCAN(y);

-    for (int stop = x + width; x < stop; x++) {

-        SkPMColor pmc = SkPreMultiplyColor(*src++);

-        *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));

-//        *d++ = SkPixel32ToPixel4444(pmc);

-    }

-}

-

-static void FromColor_D4444_Raw(void* dst, const SkColor src[], int width,

-                            int x, int y) {

-    SkPMColor16* d = (SkPMColor16*)dst;

-

-    DITHER_4444_SCAN(y);

-    for (int stop = x + width; x < stop; x++) {

-        SkColor c = *src++;

-

-        // SkPMColor is used because the ordering is ARGB32, even though the target actually premultiplied

-        SkPMColor pmc = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),

-                                            SkColorGetG(c), SkColorGetB(c));

-        *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));

-//        *d++ = SkPixel32ToPixel4444(pmc);

-    }

-}

-

-// can return NULL

-static FromColorProc ChooseFromColorProc(SkBitmap::Config config, bool isPremultiplied) {

-    switch (config) {

-        case SkBitmap::kARGB_8888_Config:

-            return isPremultiplied ? FromColor_D32 : FromColor_D32_Raw;

-        case SkBitmap::kARGB_4444_Config:

-            return isPremultiplied ? FromColor_D4444 : FromColor_D4444_Raw;

-        case SkBitmap::kRGB_565_Config:

-            return FromColor_D565;

-        default:

-            break;

-    }

-    return NULL;

-}

-

-bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride,

-        int x, int y, int width, int height,

-        const SkBitmap& dstBitmap, bool isPremultiplied) {

-    SkAutoLockPixels alp(dstBitmap);

-    void* dst = dstBitmap.getPixels();

-    FromColorProc proc = ChooseFromColorProc(dstBitmap.config(), isPremultiplied);

-

-    if (NULL == dst || NULL == proc) {

-        return false;

-    }

-

-    const jint* array = env->GetIntArrayElements(srcColors, NULL);

-    const SkColor* src = (const SkColor*)array + srcOffset;

-

-    // reset to to actual choice from caller

-    dst = dstBitmap.getAddr(x, y);

-    // now copy/convert each scanline

-    for (int y = 0; y < height; y++) {

-        proc(dst, src, width, x, y);

-        src += srcStride;

-        dst = (char*)dst + dstBitmap.rowBytes();

-    }

-

-    dstBitmap.notifyPixelsChanged();

-

-    env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),

-                                 JNI_ABORT);

-    return true;

-}

-

-//////////////////// ToColor procs

-

-typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,

-                            SkColorTable*);

-

-static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,

-                              SkColorTable*) {

-    SkASSERT(width > 0);

-    const SkPMColor* s = (const SkPMColor*)src;

-    do {

-        *dst++ = SkUnPreMultiply::PMColorToColor(*s++);

-    } while (--width != 0);

-}

-

-static void ToColor_S32_Raw(SkColor dst[], const void* src, int width,

-                              SkColorTable*) {

-    SkASSERT(width > 0);

-    const SkPMColor* s = (const SkPMColor*)src;

-    do {

-        SkPMColor c = *s++;

-        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),

-                                SkGetPackedG32(c), SkGetPackedB32(c));

-    } while (--width != 0);

-}

-

-static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,

-                               SkColorTable*) {

-    SkASSERT(width > 0);

-    const SkPMColor* s = (const SkPMColor*)src;

-    do {

-        SkPMColor c = *s++;

-        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),

-                               SkGetPackedB32(c));

-    } while (--width != 0);

-}

-

-static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,

-                                SkColorTable*) {

-    SkASSERT(width > 0);

-    const SkPMColor16* s = (const SkPMColor16*)src;

-    do {

-        *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++));

-    } while (--width != 0);

-}

-

-static void ToColor_S4444_Raw(SkColor dst[], const void* src, int width,

-                                SkColorTable*) {

-    SkASSERT(width > 0);

-    const SkPMColor16* s = (const SkPMColor16*)src;

-    do {

-        SkPMColor c = SkPixel4444ToPixel32(*s++);

-        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),

-                                SkGetPackedG32(c), SkGetPackedB32(c));

-    } while (--width != 0);

-}

-

-static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,

-                                 SkColorTable*) {

-    SkASSERT(width > 0);

-    const SkPMColor16* s = (const SkPMColor16*)src;

-    do {

-        SkPMColor c = SkPixel4444ToPixel32(*s++);

-        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),

-                               SkGetPackedB32(c));

-    } while (--width != 0);

-}

-

-static void ToColor_S565(SkColor dst[], const void* src, int width,

-                         SkColorTable*) {

-    SkASSERT(width > 0);

-    const uint16_t* s = (const uint16_t*)src;

-    do {

-        uint16_t c = *s++;

-        *dst++ =  SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c),

-                                SkPacked16ToB32(c));

-    } while (--width != 0);

-}

-

-static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width,

-                              SkColorTable* ctable) {

-    SkASSERT(width > 0);

-    const uint8_t* s = (const uint8_t*)src;

-    const SkPMColor* colors = ctable->lockColors();

-    do {

-        *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]);

-    } while (--width != 0);

-    ctable->unlockColors();

-}

-

-static void ToColor_SI8_Raw(SkColor dst[], const void* src, int width,

-                              SkColorTable* ctable) {

-    SkASSERT(width > 0);

-    const uint8_t* s = (const uint8_t*)src;

-    const SkPMColor* colors = ctable->lockColors();

-    do {

-        SkPMColor c = colors[*s++];

-        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),

-                                SkGetPackedG32(c), SkGetPackedB32(c));

-    } while (--width != 0);

-    ctable->unlockColors();

-}

-

-static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,

-                               SkColorTable* ctable) {

-    SkASSERT(width > 0);

-    const uint8_t* s = (const uint8_t*)src;

-    const SkPMColor* colors = ctable->lockColors();

-    do {

-        SkPMColor c = colors[*s++];

-        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),

-                               SkGetPackedB32(c));

-    } while (--width != 0);

-    ctable->unlockColors();

-}

-

-// can return NULL

-static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {

-    switch (src.config()) {

-        case SkBitmap::kARGB_8888_Config:

-            if (src.isOpaque()) return ToColor_S32_Opaque;

-            return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;

-        case SkBitmap::kARGB_4444_Config:

-            if (src.isOpaque()) return ToColor_S4444_Opaque;

-            return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;

-        case SkBitmap::kRGB_565_Config:

-            return ToColor_S565;

-        case SkBitmap::kIndex8_Config:

-            if (src.getColorTable() == NULL) {

-                return NULL;

-            }

-            if (src.isOpaque()) return ToColor_SI8_Opaque;

-            return isPremultiplied ? ToColor_SI8_Raw : ToColor_SI8_Alpha;

-        default:

-            break;

-    }

-    return NULL;

-}

-

-///////////////////////////////////////////////////////////////////////////////

-///////////////////////////////////////////////////////////////////////////////

-

-static int getPremulBitmapCreateFlags(bool isMutable) {

-    int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied;

-    if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable;

-    return flags;

-}

-

-static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,

-                              jint offset, jint stride, jint width, jint height,

-                              jint configHandle, jboolean isMutable) {

-    SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);

-    if (NULL != jColors) {

-        size_t n = env->GetArrayLength(jColors);

-        if (n < SkAbs32(stride) * (size_t)height) {

-            doThrowAIOOBE(env);

-            return NULL;

-        }

-    }

-

-    // ARGB_4444 is a deprecated format, convert automatically to 8888

-    if (config == SkBitmap::kARGB_4444_Config) {

-        config = SkBitmap::kARGB_8888_Config;

-    }

-

-    SkBitmap bitmap;

-    bitmap.setConfig(config, width, height);

-

-    jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);

-    if (NULL == buff) {

-        return NULL;

-    }

-

-    if (jColors != NULL) {

-        GraphicsJNI::SetPixels(env, jColors, offset, stride,

-                0, 0, width, height, bitmap, true);

-    }

-

-    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,

-            getPremulBitmapCreateFlags(isMutable), NULL, NULL);

-}

-

-static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,

-                           jint dstConfigHandle, jboolean isMutable) {

-    const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);

-    SkBitmap::Config dstConfig = static_cast<SkBitmap::Config>(dstConfigHandle);

-    SkBitmap            result;

-    JavaPixelAllocator  allocator(env);

-

+#include "SkBitmap.h"
+#include "SkPixelRef.h"
+#include "SkImageEncoder.h"
+#include "SkColorPriv.h"
+#include "GraphicsJNI.h"
+#include "SkDither.h"
+#include "SkUnPreMultiply.h"
+#include "SkStream.h"
+
+#include <binder/Parcel.h>
+#include "android_os_Parcel.h"
+#include "android_util_Binder.h"
+#include "android_nio_utils.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+#include <jni.h>
+
+#include <Caches.h>
+
+#if 0
+    #define TRACE_BITMAP(code)  code
+#else
+    #define TRACE_BITMAP(code)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Conversions to/from SkColor, for get/setPixels, and the create method, which
+// is basically like setPixels
+
+typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
+                              int x, int y);
+
+static void FromColor_D32(void* dst, const SkColor src[], int width,
+                          int, int) {
+    SkPMColor* d = (SkPMColor*)dst;
+
+    for (int i = 0; i < width; i++) {
+        *d++ = SkPreMultiplyColor(*src++);
+    }
+}
+
+static void FromColor_D32_Raw(void* dst, const SkColor src[], int width,
+                          int, int) {
+    // SkColor's ordering may be different from SkPMColor
+    if (SK_COLOR_MATCHES_PMCOLOR_BYTE_ORDER) {
+        memcpy(dst, src, width * sizeof(SkColor));
+        return;
+    }
+
+    // order isn't same, repack each pixel manually
+    SkPMColor* d = (SkPMColor*)dst;
+    for (int i = 0; i < width; i++) {
+        SkColor c = *src++;
+        *d++ = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),
+                                   SkColorGetG(c), SkColorGetB(c));
+    }
+}
+
+static void FromColor_D565(void* dst, const SkColor src[], int width,
+                           int x, int y) {
+    uint16_t* d = (uint16_t*)dst;
+
+    DITHER_565_SCAN(y);
+    for (int stop = x + width; x < stop; x++) {
+        SkColor c = *src++;
+        *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c),
+                                DITHER_VALUE(x));
+    }
+}
+
+static void FromColor_D4444(void* dst, const SkColor src[], int width,
+                            int x, int y) {
+    SkPMColor16* d = (SkPMColor16*)dst;
+
+    DITHER_4444_SCAN(y);
+    for (int stop = x + width; x < stop; x++) {
+        SkPMColor pmc = SkPreMultiplyColor(*src++);
+        *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));
+//        *d++ = SkPixel32ToPixel4444(pmc);
+    }
+}
+
+static void FromColor_D4444_Raw(void* dst, const SkColor src[], int width,
+                            int x, int y) {
+    SkPMColor16* d = (SkPMColor16*)dst;
+
+    DITHER_4444_SCAN(y);
+    for (int stop = x + width; x < stop; x++) {
+        SkColor c = *src++;
+
+        // SkPMColor is used because the ordering is ARGB32, even though the target actually premultiplied
+        SkPMColor pmc = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),
+                                            SkColorGetG(c), SkColorGetB(c));
+        *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));
+//        *d++ = SkPixel32ToPixel4444(pmc);
+    }
+}
+
+// can return NULL
+static FromColorProc ChooseFromColorProc(SkBitmap::Config config, bool isPremultiplied) {
+    switch (config) {
+        case SkBitmap::kARGB_8888_Config:
+            return isPremultiplied ? FromColor_D32 : FromColor_D32_Raw;
+        case SkBitmap::kARGB_4444_Config:
+            return isPremultiplied ? FromColor_D4444 : FromColor_D4444_Raw;
+        case SkBitmap::kRGB_565_Config:
+            return FromColor_D565;
+        default:
+            break;
+    }
+    return NULL;
+}
+
+bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride,
+        int x, int y, int width, int height,
+        const SkBitmap& dstBitmap, bool isPremultiplied) {
+    SkAutoLockPixels alp(dstBitmap);
+    void* dst = dstBitmap.getPixels();
+    FromColorProc proc = ChooseFromColorProc(dstBitmap.config(), isPremultiplied);
+
+    if (NULL == dst || NULL == proc) {
+        return false;
+    }
+
+    const jint* array = env->GetIntArrayElements(srcColors, NULL);
+    const SkColor* src = (const SkColor*)array + srcOffset;
+
+    // reset to to actual choice from caller
+    dst = dstBitmap.getAddr(x, y);
+    // now copy/convert each scanline
+    for (int y = 0; y < height; y++) {
+        proc(dst, src, width, x, y);
+        src += srcStride;
+        dst = (char*)dst + dstBitmap.rowBytes();
+    }
+
+    dstBitmap.notifyPixelsChanged();
+
+    env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),
+                                 JNI_ABORT);
+    return true;
+}
+
+//////////////////// ToColor procs
+
+typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
+                            SkColorTable*);
+
+static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,
+                              SkColorTable*) {
+    SkASSERT(width > 0);
+    const SkPMColor* s = (const SkPMColor*)src;
+    do {
+        *dst++ = SkUnPreMultiply::PMColorToColor(*s++);
+    } while (--width != 0);
+}
+
+static void ToColor_S32_Raw(SkColor dst[], const void* src, int width,
+                              SkColorTable*) {
+    SkASSERT(width > 0);
+    const SkPMColor* s = (const SkPMColor*)src;
+    do {
+        SkPMColor c = *s++;
+        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
+                                SkGetPackedG32(c), SkGetPackedB32(c));
+    } while (--width != 0);
+}
+
+static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,
+                               SkColorTable*) {
+    SkASSERT(width > 0);
+    const SkPMColor* s = (const SkPMColor*)src;
+    do {
+        SkPMColor c = *s++;
+        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
+                               SkGetPackedB32(c));
+    } while (--width != 0);
+}
+
+static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,
+                                SkColorTable*) {
+    SkASSERT(width > 0);
+    const SkPMColor16* s = (const SkPMColor16*)src;
+    do {
+        *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++));
+    } while (--width != 0);
+}
+
+static void ToColor_S4444_Raw(SkColor dst[], const void* src, int width,
+                                SkColorTable*) {
+    SkASSERT(width > 0);
+    const SkPMColor16* s = (const SkPMColor16*)src;
+    do {
+        SkPMColor c = SkPixel4444ToPixel32(*s++);
+        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
+                                SkGetPackedG32(c), SkGetPackedB32(c));
+    } while (--width != 0);
+}
+
+static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,
+                                 SkColorTable*) {
+    SkASSERT(width > 0);
+    const SkPMColor16* s = (const SkPMColor16*)src;
+    do {
+        SkPMColor c = SkPixel4444ToPixel32(*s++);
+        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
+                               SkGetPackedB32(c));
+    } while (--width != 0);
+}
+
+static void ToColor_S565(SkColor dst[], const void* src, int width,
+                         SkColorTable*) {
+    SkASSERT(width > 0);
+    const uint16_t* s = (const uint16_t*)src;
+    do {
+        uint16_t c = *s++;
+        *dst++ =  SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c),
+                                SkPacked16ToB32(c));
+    } while (--width != 0);
+}
+
+static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width,
+                              SkColorTable* ctable) {
+    SkASSERT(width > 0);
+    const uint8_t* s = (const uint8_t*)src;
+    const SkPMColor* colors = ctable->lockColors();
+    do {
+        *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]);
+    } while (--width != 0);
+    ctable->unlockColors();
+}
+
+static void ToColor_SI8_Raw(SkColor dst[], const void* src, int width,
+                              SkColorTable* ctable) {
+    SkASSERT(width > 0);
+    const uint8_t* s = (const uint8_t*)src;
+    const SkPMColor* colors = ctable->lockColors();
+    do {
+        SkPMColor c = colors[*s++];
+        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
+                                SkGetPackedG32(c), SkGetPackedB32(c));
+    } while (--width != 0);
+    ctable->unlockColors();
+}
+
+static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,
+                               SkColorTable* ctable) {
+    SkASSERT(width > 0);
+    const uint8_t* s = (const uint8_t*)src;
+    const SkPMColor* colors = ctable->lockColors();
+    do {
+        SkPMColor c = colors[*s++];
+        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
+                               SkGetPackedB32(c));
+    } while (--width != 0);
+    ctable->unlockColors();
+}
+
+// can return NULL
+static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {
+    switch (src.config()) {
+        case SkBitmap::kARGB_8888_Config:
+            if (src.isOpaque()) return ToColor_S32_Opaque;
+            return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;
+        case SkBitmap::kARGB_4444_Config:
+            if (src.isOpaque()) return ToColor_S4444_Opaque;
+            return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;
+        case SkBitmap::kRGB_565_Config:
+            return ToColor_S565;
+        case SkBitmap::kIndex8_Config:
+            if (src.getColorTable() == NULL) {
+                return NULL;
+            }
+            if (src.isOpaque()) return ToColor_SI8_Opaque;
+            return isPremultiplied ? ToColor_SI8_Raw : ToColor_SI8_Alpha;
+        default:
+            break;
+    }
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int getPremulBitmapCreateFlags(bool isMutable) {
+    int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied;
+    if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
+    return flags;
+}
+
+static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
+                              jint offset, jint stride, jint width, jint height,
+                              jint configHandle, jboolean isMutable) {
+    SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
+    if (NULL != jColors) {
+        size_t n = env->GetArrayLength(jColors);
+        if (n < SkAbs32(stride) * (size_t)height) {
+            doThrowAIOOBE(env);
+            return NULL;
+        }
+    }
+
+    // ARGB_4444 is a deprecated format, convert automatically to 8888
+    if (config == SkBitmap::kARGB_4444_Config) {
+        config = SkBitmap::kARGB_8888_Config;
+    }
+
+    SkBitmap bitmap;
+    bitmap.setConfig(config, width, height);
+
+    jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
+    if (NULL == buff) {
+        return NULL;
+    }
+
+    if (jColors != NULL) {
+        GraphicsJNI::SetPixels(env, jColors, offset, stride,
+                0, 0, width, height, bitmap, true);
+    }
+
+    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,
+            getPremulBitmapCreateFlags(isMutable), NULL, NULL);
+}
+
+static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
+                           jint dstConfigHandle, jboolean isMutable) {
+    const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
+    SkBitmap::Config dstConfig = static_cast<SkBitmap::Config>(dstConfigHandle);
+    SkBitmap            result;
+    JavaPixelAllocator  allocator(env);
+
     if (!src->copyTo(&result, SkBitmapConfigToColorType(dstConfig), &allocator)) {
-        return NULL;

-    }

-    return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),

-            getPremulBitmapCreateFlags(isMutable), NULL, NULL);

-}

-

-static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-#ifdef USE_OPENGL_RENDERER

-    if (android::uirenderer::Caches::hasInstance()) {

-        android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap);

-        return;

-    }

-#endif // USE_OPENGL_RENDERER

-    delete bitmap;

-}

-

-static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-#ifdef USE_OPENGL_RENDERER

-    if (android::uirenderer::Caches::hasInstance()) {

-        bool result;

-        result = android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);

-        return result ? JNI_TRUE : JNI_FALSE;

-    }

-#endif // USE_OPENGL_RENDERER

-    bitmap->setPixels(NULL, NULL);

-    return JNI_TRUE;

-}

-

-static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,

-        jint width, jint height, jint configHandle, jint allocSize) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);    

-    if (width * height * SkBitmap::ComputeBytesPerPixel(config) > allocSize) {

-        // done in native as there's no way to get BytesPerPixel in Java

-        doThrowIAE(env, "Bitmap not large enough to support new configuration");

-        return;

-    }

-    SkPixelRef* ref = bitmap->pixelRef();

-    SkSafeRef(ref);

-    bitmap->setConfig(config, width, height);

-    bitmap->setPixelRef(ref);

-

-    // notifyPixelsChanged will increment the generation ID even though the actual pixel data

-    // hasn't been touched. This signals the renderer that the bitmap (including width, height,

-    // and config) has changed.

-    ref->notifyPixelsChanged();

-    SkSafeUnref(ref);

-}

-

-// These must match the int values in Bitmap.java

-enum JavaEncodeFormat {

-    kJPEG_JavaEncodeFormat = 0,

-    kPNG_JavaEncodeFormat = 1,

-    kWEBP_JavaEncodeFormat = 2

-};

-

-static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,

-                                jint format, jint quality,

-                                jobject jstream, jbyteArray jstorage) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    SkImageEncoder::Type fm;

-

-    switch (format) {

-    case kJPEG_JavaEncodeFormat:

-        fm = SkImageEncoder::kJPEG_Type;

-        break;

-    case kPNG_JavaEncodeFormat:

-        fm = SkImageEncoder::kPNG_Type;

-        break;

-    case kWEBP_JavaEncodeFormat:

-        fm = SkImageEncoder::kWEBP_Type;

-        break;

-    default:

-        return JNI_FALSE;

-    }

-

-    bool success = false;

-    if (NULL != bitmap) {

-        SkAutoLockPixels alp(*bitmap);

-

-        if (NULL == bitmap->getPixels()) {

-            return JNI_FALSE;

-        }

-

-        SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);

-        if (NULL == strm) {

-            return JNI_FALSE;

-        }

-

-        SkImageEncoder* encoder = SkImageEncoder::Create(fm);

-        if (NULL != encoder) {

-            success = encoder->encodeStream(strm, *bitmap, quality);

-            delete encoder;

-        }

-        delete strm;

-    }

-    return success ? JNI_TRUE : JNI_FALSE;

-}

-

-static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    bitmap->eraseColor(color);

-}

-

-static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    return static_cast<jint>(bitmap->rowBytes());

-}

-

-static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    return static_cast<jint>(bitmap->config());

-}

-

-static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    return static_cast<jint>(bitmap->getGenerationID());

-}

-

-static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE;

-}

-

-static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,

-                                            jboolean hasAlpha, jboolean isPremul) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    if (!hasAlpha) {

-        bitmap->setAlphaType(kOpaque_SkAlphaType);

-    } else if (isPremul) {

-        bitmap->setAlphaType(kPremul_SkAlphaType);

-    } else {

-        bitmap->setAlphaType(kUnpremul_SkAlphaType);

-    }

-}

-

-static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE;

-}

-

-static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle,

-                                jboolean hasMipMap) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    bitmap->setHasHardwareMipMap(hasMipMap);

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

-static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {

-    if (parcel == NULL) {

-        SkDebugf("-------- unparcel parcel is NULL\n");

-        return NULL;

-    }

-

-    android::Parcel* p = android::parcelForJavaObject(env, parcel);

-

-    const bool              isMutable = p->readInt32() != 0;

-    const SkBitmap::Config  config = (SkBitmap::Config)p->readInt32();

-    const int               width = p->readInt32();

-    const int               height = p->readInt32();

-    const int               rowBytes = p->readInt32();

-    const int               density = p->readInt32();

-

-    if (SkBitmap::kARGB_8888_Config != config &&

-            SkBitmap::kRGB_565_Config != config &&

-            SkBitmap::kARGB_4444_Config != config &&

-            SkBitmap::kIndex8_Config != config &&

-            SkBitmap::kA8_Config != config) {

-        SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);

-        return NULL;

-    }

-

-    SkBitmap* bitmap = new SkBitmap;

-

-    bitmap->setConfig(config, width, height, rowBytes);

-

-    SkColorTable* ctable = NULL;

-    if (config == SkBitmap::kIndex8_Config) {

-        int count = p->readInt32();

-        if (count > 0) {

-            size_t size = count * sizeof(SkPMColor);

-            const SkPMColor* src = (const SkPMColor*)p->readInplace(size);

-            ctable = new SkColorTable(src, count);

-        }

-    }

-

-    jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);

-    if (NULL == buffer) {

-        SkSafeUnref(ctable);

-        delete bitmap;

-        return NULL;

-    }

-

-    SkSafeUnref(ctable);

-

-    size_t size = bitmap->getSize();

-

-    android::Parcel::ReadableBlob blob;

-    android::status_t status = p->readBlob(size, &blob);

-    if (status) {

-        doThrowRE(env, "Could not read bitmap from parcel blob.");

-        delete bitmap;

-        return NULL;

-    }

-

-    bitmap->lockPixels();

-    memcpy(bitmap->getPixels(), blob.data(), size);

-    bitmap->unlockPixels();

-

-    blob.release();

-

-    return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable),

-            NULL, NULL, density);

-}

-

-static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,

-                                     jlong bitmapHandle,

-                                     jboolean isMutable, jint density,

-                                     jobject parcel) {

-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    if (parcel == NULL) {

-        SkDebugf("------- writeToParcel null parcel\n");

-        return JNI_FALSE;

-    }

-

-    android::Parcel* p = android::parcelForJavaObject(env, parcel);

-

-    p->writeInt32(isMutable);

-    p->writeInt32(bitmap->config());

-    p->writeInt32(bitmap->width());

-    p->writeInt32(bitmap->height());

-    p->writeInt32(bitmap->rowBytes());

-    p->writeInt32(density);

-

-    if (bitmap->config() == SkBitmap::kIndex8_Config) {

-        SkColorTable* ctable = bitmap->getColorTable();

-        if (ctable != NULL) {

-            int count = ctable->count();

-            p->writeInt32(count);

-            memcpy(p->writeInplace(count * sizeof(SkPMColor)),

-                   ctable->lockColors(), count * sizeof(SkPMColor));

-            ctable->unlockColors();

-        } else {

-            p->writeInt32(0);   // indicate no ctable

-        }

-    }

-

-    size_t size = bitmap->getSize();

-

-    android::Parcel::WritableBlob blob;

-    android::status_t status = p->writeBlob(size, &blob);

-    if (status) {

-        doThrowRE(env, "Could not write bitmap to parcel blob.");

-        return JNI_FALSE;

-    }

-

-    bitmap->lockPixels();

-    const void* pSrc =  bitmap->getPixels();

-    if (pSrc == NULL) {

-        memset(blob.data(), 0, size);

-    } else {

-        memcpy(blob.data(), pSrc, size);

-    }

-    bitmap->unlockPixels();

-

-    blob.release();

-    return JNI_TRUE;

-}

-

-static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,

-                                   jlong srcHandle, jlong paintHandle,

-                                   jintArray offsetXY) {

-    const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);

-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);

-    SkIPoint  offset;

-    SkBitmap* dst = new SkBitmap;

-    JavaPixelAllocator allocator(env);

-

-    src->extractAlpha(dst, paint, &allocator, &offset);

-    // If Skia can't allocate pixels for destination bitmap, it resets

-    // it, that is set its pixels buffer to NULL, and zero width and height.

-    if (dst->getPixels() == NULL && src->getPixels() != NULL) {

-        delete dst;

-        doThrowOOME(env, "failed to allocate pixels for alpha");

-        return NULL;

-    }

-    if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {

-        int* array = env->GetIntArrayElements(offsetXY, NULL);

-        array[0] = offset.fX;

-        array[1] = offset.fY;

-        env->ReleaseIntArrayElements(offsetXY, array, 0);

-    }

-

-    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),

-            getPremulBitmapCreateFlags(true), NULL, NULL);

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

-static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,

-        jint x, jint y, jboolean isPremultiplied) {

-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    SkAutoLockPixels alp(*bitmap);

-

-    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);

-    if (NULL == proc) {

-        return 0;

-    }

-    const void* src = bitmap->getAddr(x, y);

-    if (NULL == src) {

-        return 0;

-    }

-

-    SkColor dst[1];

-    proc(dst, src, 1, bitmap->getColorTable());

-    return static_cast<jint>(dst[0]);

-}

-

-static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,

-        jintArray pixelArray, jint offset, jint stride,

-        jint x, jint y, jint width, jint height, jboolean isPremultiplied) {

-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    SkAutoLockPixels alp(*bitmap);

-

-    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);

-    if (NULL == proc) {

-        return;

-    }

-    const void* src = bitmap->getAddr(x, y);

-    if (NULL == src) {

-        return;

-    }

-

-    SkColorTable* ctable = bitmap->getColorTable();

-    jint* dst = env->GetIntArrayElements(pixelArray, NULL);

-    SkColor* d = (SkColor*)dst + offset;

-    while (--height >= 0) {

-        proc(d, src, width, ctable);

-        d += stride;

-        src = (void*)((const char*)src + bitmap->rowBytes());

-    }

-    env->ReleaseIntArrayElements(pixelArray, dst, 0);

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

-static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,

-        jint x, jint y, jint colorHandle, jboolean isPremultiplied) {

-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    SkColor color = static_cast<SkColor>(colorHandle);

-    SkAutoLockPixels alp(*bitmap);

-    if (NULL == bitmap->getPixels()) {

-        return;

-    }

-

-    FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);

-    if (NULL == proc) {

-        return;

-    }

-

-    proc(bitmap->getAddr(x, y), &color, 1, x, y);

-    bitmap->notifyPixelsChanged();

-}

-

-static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle,

-        jintArray pixelArray, jint offset, jint stride,

-        jint x, jint y, jint width, jint height, jboolean isPremultiplied) {

-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    GraphicsJNI::SetPixels(env, pixelArray, offset, stride,

-            x, y, width, height, *bitmap, isPremultiplied);

-}

-

-static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,

-                                      jlong bitmapHandle, jobject jbuffer) {

-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    SkAutoLockPixels alp(*bitmap);

-    const void* src = bitmap->getPixels();

-

-    if (NULL != src) {

-        android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);

-

-        // the java side has already checked that buffer is large enough

-        memcpy(abp.pointer(), src, bitmap->getSize());

-    }

-}

-

-static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,

-                                        jlong bitmapHandle, jobject jbuffer) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    SkAutoLockPixels alp(*bitmap);

-    void* dst = bitmap->getPixels();

-

-    if (NULL != dst) {

-        android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);

-        // the java side has already checked that buffer is large enough

-        memcpy(dst, abp.pointer(), bitmap->getSize());

-        bitmap->notifyPixelsChanged();

-    }

-}

-

-static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,

-                              jlong bm1Handle) {

-    const SkBitmap* bm0 = reinterpret_cast<SkBitmap*>(bm0Handle);

-    const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle);

-    if (bm0->width() != bm1->width() ||

-        bm0->height() != bm1->height() ||

-        bm0->config() != bm1->config()) {

-        return JNI_FALSE;

-    }

-

-    SkAutoLockPixels alp0(*bm0);

-    SkAutoLockPixels alp1(*bm1);

-

-    // if we can't load the pixels, return false

-    if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {

-        return JNI_FALSE;

-    }

-

-    if (bm0->config() == SkBitmap::kIndex8_Config) {

-        SkColorTable* ct0 = bm0->getColorTable();

-        SkColorTable* ct1 = bm1->getColorTable();

-        if (NULL == ct0 || NULL == ct1) {

-            return JNI_FALSE;

-        }

-        if (ct0->count() != ct1->count()) {

-            return JNI_FALSE;

-        }

-

-        SkAutoLockColors alc0(ct0);

-        SkAutoLockColors alc1(ct1);

-        const size_t size = ct0->count() * sizeof(SkPMColor);

-        if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {

-            return JNI_FALSE;

-        }

-    }

-

-    // now compare each scanline. We can't do the entire buffer at once,

-    // since we don't care about the pixel values that might extend beyond

-    // the width (since the scanline might be larger than the logical width)

-    const int h = bm0->height();

-    const size_t size = bm0->width() * bm0->bytesPerPixel();

-    for (int y = 0; y < h; y++) {

-        if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {

-            return JNI_FALSE;

-        }

-    }

-    return JNI_TRUE;

-}

-

-static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    bitmap->lockPixels();

-    bitmap->unlockPixels();

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

-#include <android_runtime/AndroidRuntime.h>

-

-static JNINativeMethod gBitmapMethods[] = {

-    {   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",

-        (void*)Bitmap_creator },

-    {   "nativeCopy",               "(JIZ)Landroid/graphics/Bitmap;",

-        (void*)Bitmap_copy },

-    {   "nativeDestructor",         "(J)V", (void*)Bitmap_destructor },

-    {   "nativeRecycle",            "(J)Z", (void*)Bitmap_recycle },

-    {   "nativeReconfigure",        "(JIIII)V", (void*)Bitmap_reconfigure },

-    {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",

-        (void*)Bitmap_compress },

-    {   "nativeErase",              "(JI)V", (void*)Bitmap_erase },

-    {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },

-    {   "nativeConfig",             "(J)I", (void*)Bitmap_config },

-    {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },

-    {   "nativeSetAlphaAndPremultiplied", "(JZZ)V", (void*)Bitmap_setAlphaAndPremultiplied},

-    {   "nativeHasMipMap",          "(J)Z", (void*)Bitmap_hasMipMap },

-    {   "nativeSetHasMipMap",       "(JZ)V", (void*)Bitmap_setHasMipMap },

-    {   "nativeCreateFromParcel",

-        "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",

-        (void*)Bitmap_createFromParcel },

-    {   "nativeWriteToParcel",      "(JZILandroid/os/Parcel;)Z",

-        (void*)Bitmap_writeToParcel },

-    {   "nativeExtractAlpha",       "(JJ[I)Landroid/graphics/Bitmap;",

-        (void*)Bitmap_extractAlpha },

-    {   "nativeGenerationId",       "(J)I", (void*)Bitmap_getGenerationId },

-    {   "nativeGetPixel",           "(JIIZ)I", (void*)Bitmap_getPixel },

-    {   "nativeGetPixels",          "(J[IIIIIIIZ)V", (void*)Bitmap_getPixels },

-    {   "nativeSetPixel",           "(JIIIZ)V", (void*)Bitmap_setPixel },

-    {   "nativeSetPixels",          "(J[IIIIIIIZ)V", (void*)Bitmap_setPixels },

-    {   "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",

-                                            (void*)Bitmap_copyPixelsToBuffer },

-    {   "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",

-                                            (void*)Bitmap_copyPixelsFromBuffer },

-    {   "nativeSameAs",             "(JJ)Z", (void*)Bitmap_sameAs },

-    {   "nativePrepareToDraw",      "(J)V", (void*)Bitmap_prepareToDraw },

-};

-

-#define kClassPathName  "android/graphics/Bitmap"

-

-int register_android_graphics_Bitmap(JNIEnv* env)

-{

-    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,

-                                gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));

-}

+        return NULL;
+    }
+    return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),
+            getPremulBitmapCreateFlags(isMutable), NULL, NULL);
+}
+
+static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+#ifdef USE_OPENGL_RENDERER
+    if (android::uirenderer::Caches::hasInstance()) {
+        android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap);
+        return;
+    }
+#endif // USE_OPENGL_RENDERER
+    delete bitmap;
+}
+
+static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+#ifdef USE_OPENGL_RENDERER
+    if (android::uirenderer::Caches::hasInstance()) {
+        bool result;
+        result = android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);
+        return result ? JNI_TRUE : JNI_FALSE;
+    }
+#endif // USE_OPENGL_RENDERER
+    bitmap->setPixels(NULL, NULL);
+    return JNI_TRUE;
+}
+
+static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,
+        jint width, jint height, jint configHandle, jint allocSize) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
+    if (width * height * SkBitmap::ComputeBytesPerPixel(config) > allocSize) {
+        // done in native as there's no way to get BytesPerPixel in Java
+        doThrowIAE(env, "Bitmap not large enough to support new configuration");
+        return;
+    }
+    SkPixelRef* ref = bitmap->pixelRef();
+    SkSafeRef(ref);
+    bitmap->setConfig(config, width, height);
+    bitmap->setPixelRef(ref);
+
+    // notifyPixelsChanged will increment the generation ID even though the actual pixel data
+    // hasn't been touched. This signals the renderer that the bitmap (including width, height,
+    // and config) has changed.
+    ref->notifyPixelsChanged();
+    SkSafeUnref(ref);
+}
+
+// These must match the int values in Bitmap.java
+enum JavaEncodeFormat {
+    kJPEG_JavaEncodeFormat = 0,
+    kPNG_JavaEncodeFormat = 1,
+    kWEBP_JavaEncodeFormat = 2
+};
+
+static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,
+                                jint format, jint quality,
+                                jobject jstream, jbyteArray jstorage) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkImageEncoder::Type fm;
+
+    switch (format) {
+    case kJPEG_JavaEncodeFormat:
+        fm = SkImageEncoder::kJPEG_Type;
+        break;
+    case kPNG_JavaEncodeFormat:
+        fm = SkImageEncoder::kPNG_Type;
+        break;
+    case kWEBP_JavaEncodeFormat:
+        fm = SkImageEncoder::kWEBP_Type;
+        break;
+    default:
+        return JNI_FALSE;
+    }
+
+    bool success = false;
+    if (NULL != bitmap) {
+        SkAutoLockPixels alp(*bitmap);
+
+        if (NULL == bitmap->getPixels()) {
+            return JNI_FALSE;
+        }
+
+        SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
+        if (NULL == strm) {
+            return JNI_FALSE;
+        }
+
+        SkImageEncoder* encoder = SkImageEncoder::Create(fm);
+        if (NULL != encoder) {
+            success = encoder->encodeStream(strm, *bitmap, quality);
+            delete encoder;
+        }
+        delete strm;
+    }
+    return success ? JNI_TRUE : JNI_FALSE;
+}
+
+static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    bitmap->eraseColor(color);
+}
+
+static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    return static_cast<jint>(bitmap->rowBytes());
+}
+
+static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    return static_cast<jint>(bitmap->config());
+}
+
+static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    return static_cast<jint>(bitmap->getGenerationID());
+}
+
+static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE;
+}
+
+static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,
+                                            jboolean hasAlpha, jboolean isPremul) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    if (!hasAlpha) {
+        bitmap->setAlphaType(kOpaque_SkAlphaType);
+    } else if (isPremul) {
+        bitmap->setAlphaType(kPremul_SkAlphaType);
+    } else {
+        bitmap->setAlphaType(kUnpremul_SkAlphaType);
+    }
+}
+
+static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE;
+}
+
+static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle,
+                                jboolean hasMipMap) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    bitmap->setHasHardwareMipMap(hasMipMap);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
+    if (parcel == NULL) {
+        SkDebugf("-------- unparcel parcel is NULL\n");
+        return NULL;
+    }
+
+    android::Parcel* p = android::parcelForJavaObject(env, parcel);
+
+    const bool              isMutable = p->readInt32() != 0;
+    const SkBitmap::Config  config = (SkBitmap::Config)p->readInt32();
+    const int               width = p->readInt32();
+    const int               height = p->readInt32();
+    const int               rowBytes = p->readInt32();
+    const int               density = p->readInt32();
+
+    if (SkBitmap::kARGB_8888_Config != config &&
+            SkBitmap::kRGB_565_Config != config &&
+            SkBitmap::kARGB_4444_Config != config &&
+            SkBitmap::kIndex8_Config != config &&
+            SkBitmap::kA8_Config != config) {
+        SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
+        return NULL;
+    }
+
+    SkBitmap* bitmap = new SkBitmap;
+
+    bitmap->setConfig(config, width, height, rowBytes);
+
+    SkColorTable* ctable = NULL;
+    if (config == SkBitmap::kIndex8_Config) {
+        int count = p->readInt32();
+        if (count > 0) {
+            size_t size = count * sizeof(SkPMColor);
+            const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
+            ctable = new SkColorTable(src, count);
+        }
+    }
+
+    jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
+    if (NULL == buffer) {
+        SkSafeUnref(ctable);
+        delete bitmap;
+        return NULL;
+    }
+
+    SkSafeUnref(ctable);
+
+    size_t size = bitmap->getSize();
+
+    android::Parcel::ReadableBlob blob;
+    android::status_t status = p->readBlob(size, &blob);
+    if (status) {
+        doThrowRE(env, "Could not read bitmap from parcel blob.");
+        delete bitmap;
+        return NULL;
+    }
+
+    bitmap->lockPixels();
+    memcpy(bitmap->getPixels(), blob.data(), size);
+    bitmap->unlockPixels();
+
+    blob.release();
+
+    return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable),
+            NULL, NULL, density);
+}
+
+static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
+                                     jlong bitmapHandle,
+                                     jboolean isMutable, jint density,
+                                     jobject parcel) {
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    if (parcel == NULL) {
+        SkDebugf("------- writeToParcel null parcel\n");
+        return JNI_FALSE;
+    }
+
+    android::Parcel* p = android::parcelForJavaObject(env, parcel);
+
+    p->writeInt32(isMutable);
+    p->writeInt32(bitmap->config());
+    p->writeInt32(bitmap->width());
+    p->writeInt32(bitmap->height());
+    p->writeInt32(bitmap->rowBytes());
+    p->writeInt32(density);
+
+    if (bitmap->config() == SkBitmap::kIndex8_Config) {
+        SkColorTable* ctable = bitmap->getColorTable();
+        if (ctable != NULL) {
+            int count = ctable->count();
+            p->writeInt32(count);
+            memcpy(p->writeInplace(count * sizeof(SkPMColor)),
+                   ctable->lockColors(), count * sizeof(SkPMColor));
+            ctable->unlockColors();
+        } else {
+            p->writeInt32(0);   // indicate no ctable
+        }
+    }
+
+    size_t size = bitmap->getSize();
+
+    android::Parcel::WritableBlob blob;
+    android::status_t status = p->writeBlob(size, &blob);
+    if (status) {
+        doThrowRE(env, "Could not write bitmap to parcel blob.");
+        return JNI_FALSE;
+    }
+
+    bitmap->lockPixels();
+    const void* pSrc =  bitmap->getPixels();
+    if (pSrc == NULL) {
+        memset(blob.data(), 0, size);
+    } else {
+        memcpy(blob.data(), pSrc, size);
+    }
+    bitmap->unlockPixels();
+
+    blob.release();
+    return JNI_TRUE;
+}
+
+static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
+                                   jlong srcHandle, jlong paintHandle,
+                                   jintArray offsetXY) {
+    const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    SkIPoint  offset;
+    SkBitmap* dst = new SkBitmap;
+    JavaPixelAllocator allocator(env);
+
+    src->extractAlpha(dst, paint, &allocator, &offset);
+    // If Skia can't allocate pixels for destination bitmap, it resets
+    // it, that is set its pixels buffer to NULL, and zero width and height.
+    if (dst->getPixels() == NULL && src->getPixels() != NULL) {
+        delete dst;
+        doThrowOOME(env, "failed to allocate pixels for alpha");
+        return NULL;
+    }
+    if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
+        int* array = env->GetIntArrayElements(offsetXY, NULL);
+        array[0] = offset.fX;
+        array[1] = offset.fY;
+        env->ReleaseIntArrayElements(offsetXY, array, 0);
+    }
+
+    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),
+            getPremulBitmapCreateFlags(true), NULL, NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
+        jint x, jint y, jboolean isPremultiplied) {
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkAutoLockPixels alp(*bitmap);
+
+    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
+    if (NULL == proc) {
+        return 0;
+    }
+    const void* src = bitmap->getAddr(x, y);
+    if (NULL == src) {
+        return 0;
+    }
+
+    SkColor dst[1];
+    proc(dst, src, 1, bitmap->getColorTable());
+    return static_cast<jint>(dst[0]);
+}
+
+static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,
+        jintArray pixelArray, jint offset, jint stride,
+        jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkAutoLockPixels alp(*bitmap);
+
+    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
+    if (NULL == proc) {
+        return;
+    }
+    const void* src = bitmap->getAddr(x, y);
+    if (NULL == src) {
+        return;
+    }
+
+    SkColorTable* ctable = bitmap->getColorTable();
+    jint* dst = env->GetIntArrayElements(pixelArray, NULL);
+    SkColor* d = (SkColor*)dst + offset;
+    while (--height >= 0) {
+        proc(d, src, width, ctable);
+        d += stride;
+        src = (void*)((const char*)src + bitmap->rowBytes());
+    }
+    env->ReleaseIntArrayElements(pixelArray, dst, 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,
+        jint x, jint y, jint colorHandle, jboolean isPremultiplied) {
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkColor color = static_cast<SkColor>(colorHandle);
+    SkAutoLockPixels alp(*bitmap);
+    if (NULL == bitmap->getPixels()) {
+        return;
+    }
+
+    FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);
+    if (NULL == proc) {
+        return;
+    }
+
+    proc(bitmap->getAddr(x, y), &color, 1, x, y);
+    bitmap->notifyPixelsChanged();
+}
+
+static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle,
+        jintArray pixelArray, jint offset, jint stride,
+        jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
+            x, y, width, height, *bitmap, isPremultiplied);
+}
+
+static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
+                                      jlong bitmapHandle, jobject jbuffer) {
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkAutoLockPixels alp(*bitmap);
+    const void* src = bitmap->getPixels();
+
+    if (NULL != src) {
+        android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
+
+        // the java side has already checked that buffer is large enough
+        memcpy(abp.pointer(), src, bitmap->getSize());
+    }
+}
+
+static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
+                                        jlong bitmapHandle, jobject jbuffer) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkAutoLockPixels alp(*bitmap);
+    void* dst = bitmap->getPixels();
+
+    if (NULL != dst) {
+        android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
+        // the java side has already checked that buffer is large enough
+        memcpy(dst, abp.pointer(), bitmap->getSize());
+        bitmap->notifyPixelsChanged();
+    }
+}
+
+static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,
+                              jlong bm1Handle) {
+    const SkBitmap* bm0 = reinterpret_cast<SkBitmap*>(bm0Handle);
+    const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle);
+    if (bm0->width() != bm1->width() ||
+        bm0->height() != bm1->height() ||
+        bm0->config() != bm1->config()) {
+        return JNI_FALSE;
+    }
+
+    SkAutoLockPixels alp0(*bm0);
+    SkAutoLockPixels alp1(*bm1);
+
+    // if we can't load the pixels, return false
+    if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
+        return JNI_FALSE;
+    }
+
+    if (bm0->config() == SkBitmap::kIndex8_Config) {
+        SkColorTable* ct0 = bm0->getColorTable();
+        SkColorTable* ct1 = bm1->getColorTable();
+        if (NULL == ct0 || NULL == ct1) {
+            return JNI_FALSE;
+        }
+        if (ct0->count() != ct1->count()) {
+            return JNI_FALSE;
+        }
+
+        SkAutoLockColors alc0(ct0);
+        SkAutoLockColors alc1(ct1);
+        const size_t size = ct0->count() * sizeof(SkPMColor);
+        if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {
+            return JNI_FALSE;
+        }
+    }
+
+    // now compare each scanline. We can't do the entire buffer at once,
+    // since we don't care about the pixel values that might extend beyond
+    // the width (since the scanline might be larger than the logical width)
+    const int h = bm0->height();
+    const size_t size = bm0->width() * bm0->bytesPerPixel();
+    for (int y = 0; y < h; y++) {
+        if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {
+            return JNI_FALSE;
+        }
+    }
+    return JNI_TRUE;
+}
+
+static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    bitmap->lockPixels();
+    bitmap->unlockPixels();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+static JNINativeMethod gBitmapMethods[] = {
+    {   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_creator },
+    {   "nativeCopy",               "(JIZ)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_copy },
+    {   "nativeDestructor",         "(J)V", (void*)Bitmap_destructor },
+    {   "nativeRecycle",            "(J)Z", (void*)Bitmap_recycle },
+    {   "nativeReconfigure",        "(JIIII)V", (void*)Bitmap_reconfigure },
+    {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",
+        (void*)Bitmap_compress },
+    {   "nativeErase",              "(JI)V", (void*)Bitmap_erase },
+    {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },
+    {   "nativeConfig",             "(J)I", (void*)Bitmap_config },
+    {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },
+    {   "nativeSetAlphaAndPremultiplied", "(JZZ)V", (void*)Bitmap_setAlphaAndPremultiplied},
+    {   "nativeHasMipMap",          "(J)Z", (void*)Bitmap_hasMipMap },
+    {   "nativeSetHasMipMap",       "(JZ)V", (void*)Bitmap_setHasMipMap },
+    {   "nativeCreateFromParcel",
+        "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_createFromParcel },
+    {   "nativeWriteToParcel",      "(JZILandroid/os/Parcel;)Z",
+        (void*)Bitmap_writeToParcel },
+    {   "nativeExtractAlpha",       "(JJ[I)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_extractAlpha },
+    {   "nativeGenerationId",       "(J)I", (void*)Bitmap_getGenerationId },
+    {   "nativeGetPixel",           "(JIIZ)I", (void*)Bitmap_getPixel },
+    {   "nativeGetPixels",          "(J[IIIIIIIZ)V", (void*)Bitmap_getPixels },
+    {   "nativeSetPixel",           "(JIIIZ)V", (void*)Bitmap_setPixel },
+    {   "nativeSetPixels",          "(J[IIIIIIIZ)V", (void*)Bitmap_setPixels },
+    {   "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",
+                                            (void*)Bitmap_copyPixelsToBuffer },
+    {   "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",
+                                            (void*)Bitmap_copyPixelsFromBuffer },
+    {   "nativeSameAs",             "(JJ)Z", (void*)Bitmap_sameAs },
+    {   "nativePrepareToDraw",      "(J)V", (void*)Bitmap_prepareToDraw },
+};
+
+#define kClassPathName  "android/graphics/Bitmap"
+
+int register_android_graphics_Bitmap(JNIEnv* env)
+{
+    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+                                gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
+}
diff --git a/core/jni/android/graphics/CanvasProperty.cpp b/core/jni/android/graphics/CanvasProperty.cpp
index 70e2db5..cfa9cd8 100644
--- a/core/jni/android/graphics/CanvasProperty.cpp
+++ b/core/jni/android/graphics/CanvasProperty.cpp
@@ -18,7 +18,7 @@
 #include "GraphicsJNI.h"
 #include <android_runtime/AndroidRuntime.h>
 
-#include <utils/VirtualLightRefBase.h>
+#include <utils/RefBase.h>
 #include <CanvasProperty.h>
 
 namespace android {
@@ -27,22 +27,13 @@
 
 #ifdef USE_OPENGL_RENDERER
 
-static jlong incRef(VirtualLightRefBase* ptr) {
-    ptr->incStrong(0);
-    return reinterpret_cast<jlong>(ptr);
-}
-
 static jlong createFloat(JNIEnv* env, jobject clazz, jfloat initialValue) {
-    return incRef(new CanvasPropertyPrimitive(initialValue));
+    return reinterpret_cast<jlong>(new CanvasPropertyPrimitive(initialValue));
 }
 
 static jlong createPaint(JNIEnv* env, jobject clazz, jlong paintPtr) {
     const SkPaint* paint = reinterpret_cast<const SkPaint*>(paintPtr);
-    return incRef(new CanvasPropertyPaint(*paint));
-}
-
-static void unref(JNIEnv* env, jobject clazz, jlong containerPtr) {
-    reinterpret_cast<VirtualLightRefBase*>(containerPtr)->decStrong(0);
+    return reinterpret_cast<jlong>(new CanvasPropertyPaint(*paint));
 }
 
 #endif
@@ -57,7 +48,6 @@
 #ifdef USE_OPENGL_RENDERER
     { "nCreateFloat", "(F)J", (void*) createFloat },
     { "nCreatePaint", "(J)J", (void*) createPaint },
-    { "nUnref", "(J)V", (void*) unref },
 #endif
 };
 
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index 3be013b..4787d28 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -101,7 +101,6 @@
     RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw);
 
     BaseAnimator* animator = new RenderPropertyAnimator(property, deltaType, deltaValue);
-    animator->incStrong(0);
     animator->setListener(new AnimationListenerBridge(env, weakThis));
     return reinterpret_cast<jlong>( animator );
 }
@@ -111,7 +110,6 @@
     RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw);
     CanvasPropertyPrimitive* canvasProperty = reinterpret_cast<CanvasPropertyPrimitive*>(canvasPropertyPtr);
     BaseAnimator* animator = new CanvasPropertyPrimitiveAnimator(canvasProperty, deltaType, deltaValue);
-    animator->incStrong(0);
     animator->setListener(new AnimationListenerBridge(env, weakThis));
     return reinterpret_cast<jlong>( animator );
 }
@@ -124,7 +122,6 @@
     CanvasPropertyPaintAnimator::PaintField paintField = toPaintField(paintFieldRaw);
     BaseAnimator* animator = new CanvasPropertyPaintAnimator(
             canvasProperty, paintField, deltaType, deltaValue);
-    animator->incStrong(0);
     animator->setListener(new AnimationListenerBridge(env, weakThis));
     return reinterpret_cast<jlong>( animator );
 }
@@ -135,11 +132,6 @@
     animator->setDuration(duration);
 }
 
-static void unref(JNIEnv* env, jobject clazz, jlong objPtr) {
-    VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr);
-    obj->decStrong(0);
-}
-
 #endif
 
 // ----------------------------------------------------------------------------
@@ -154,7 +146,6 @@
     { "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyFloatAnimator },
     { "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIIF)J", (void*) createCanvasPropertyPaintAnimator },
     { "nSetDuration", "(JI)V", (void*) setDuration },
-    { "nUnref", "(J)V", (void*) unref },
 #endif
 };
 
diff --git a/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp b/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp
new file mode 100644
index 0000000..ce6f207
--- /dev/null
+++ b/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+namespace android {
+
+static void incStrong(JNIEnv* env, jobject clazz, jlong objPtr) {
+    VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr);
+    obj->incStrong(0);
+}
+
+static void decStrong(JNIEnv* env, jobject clazz, jlong objPtr) {
+    VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr);
+    obj->decStrong(0);
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "com/android/internal/util/VirtualRefBasePtr";
+
+static JNINativeMethod gMethods[] = {
+    { "nIncStrong", "(J)V", (void*) incStrong },
+    { "nDecStrong", "(J)V", (void*) decStrong },
+};
+
+int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv* env) {
+    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+
+} // namespace android
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 28e75e6..7a6832e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6605,4 +6605,8 @@
         <attr name="layout_gravity" />
     </declare-styleable>
 
+    <!-- Used as a filter array on the theme to pull out only the EdgeEffect-relevant bits. -->
+    <declare-styleable name="EdgeEffect">
+        <attr name="colorPrimaryLight" />
+    </declare-styleable>
 </resources>
diff --git a/docs/html/guide/topics/resources/string-resource.jd b/docs/html/guide/topics/resources/string-resource.jd
index 5a96ba1..e2326ec 100644
--- a/docs/html/guide/topics/resources/string-resource.jd
+++ b/docs/html/guide/topics/resources/string-resource.jd
@@ -20,9 +20,6 @@
 information about styling and formatting strings, see the section about <a
 href="#FormattingAndStyling">Formatting and Styling</a>.</p>
 
-
-
-
 <h2 id="String">String</h2>
 
 <p>A single string that can be referenced from the application or from other resource files (such
@@ -433,7 +430,7 @@
 
 
 
-<h3>Styling with HTML markup</h3>
+<h3 id="StylingWithHTML">Styling with HTML markup</h3>
 
 <p>You can add styling to your strings with HTML markup. For example:</p>
 <pre>
@@ -497,5 +494,107 @@
 CharSequence styledText = Html.fromHtml(text);
 </pre>
 
+<h2 id="StylingWithSpannables">Styling with Spannables</h2>
+<p>
+A {@link android.text.Spannable} is a text object that you can style with
+typeface properties such as color and font weight. You use
+{@link android.text.SpannableStringBuilder} to build
+your text and then apply styles defined in the {@link android.text.style}
+package to the text.
+</p>
 
+<p>You can use the following helper methods to set up much of the work
+of creating spannable text:</p>
 
+<pre style="pretty-print">
+/**
+ * Returns a CharSequence that concatenates the specified array of CharSequence
+ * objects and then applies a list of zero or more tags to the entire range.
+ *
+ * @param content an array of character sequences to apply a style to
+ * @param tags the styled span objects to apply to the content
+ *        such as android.text.style.StyleSpan
+ *
+ */
+private static CharSequence apply(CharSequence[] content, Object... tags) {
+    SpannableStringBuilder text = new SpannableStringBuilder();
+    openTags(text, tags);
+    for (CharSequence item : content) {
+        text.append(item);
+    }
+    closeTags(text, tags);
+    return text;
+}
+
+/**
+ * Iterates over an array of tags and applies them to the beginning of the specified
+ * Spannable object so that future text appended to the text will have the styling
+ * applied to it. Do not call this method directly.
+ */
+private static void openTags(Spannable text, Object[] tags) {
+    for (Object tag : tags) {
+        text.setSpan(tag, 0, 0, Spannable.SPAN_MARK_MARK);
+    }
+}
+
+/**
+ * "Closes" the specified tags on a Spannable by updating the spans to be
+ * endpoint-exclusive so that future text appended to the end will not take
+ * on the same styling. Do not call this method directly.
+ */
+private static void closeTags(Spannable text, Object[] tags) {
+    int len = text.length();
+    for (Object tag : tags) {
+        if (len > 0) {
+            text.setSpan(tag, 0, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        } else {
+            text.removeSpan(tag);
+        }
+    }
+}
+</pre>
+
+<p>
+The following <code>bold</code>, <code>italic</code>, and <code>color</code>
+methods show you how to call the helper methods to apply
+styles defined in the {@link android.text.style} package. You
+can create similar methods to do other types of text styling.
+</p>
+
+<pre style="pretty-print">
+/**
+ * Returns a CharSequence that applies boldface to the concatenation
+ * of the specified CharSequence objects.
+ */
+public static CharSequence bold(CharSequence... content) {
+    return apply(content, new StyleSpan(Typeface.BOLD));
+}
+
+/**
+ * Returns a CharSequence that applies italics to the concatenation
+ * of the specified CharSequence objects.
+ */
+public static CharSequence italic(CharSequence... content) {
+    return apply(content, new StyleSpan(Typeface.ITALIC));
+}
+
+/**
+ * Returns a CharSequence that applies a foreground color to the
+ * concatenation of the specified CharSequence objects.
+ */
+public static CharSequence color(int color, CharSequence... content) {
+    return apply(content, new ForegroundColorSpan(color));
+}
+</pre>
+
+<p>
+Here's an example of how to chain these methods to create a character sequence
+with different types of styling applied to individual words:
+</p>
+
+<pre style="pretty-print">
+// Create an italic "hello, " a red "world",
+// and bold the entire sequence.
+CharSequence text = bold(italic(res.getString(R.string.hello)),
+    color(Color.RED, res.getString(R.string.world)));
+</pre>
\ No newline at end of file
diff --git a/docs/html/guide/topics/ui/notifiers/notifications.jd b/docs/html/guide/topics/ui/notifiers/notifications.jd
index 3b1292e..59c2269 100644
--- a/docs/html/guide/topics/ui/notifiers/notifications.jd
+++ b/docs/html/guide/topics/ui/notifiers/notifications.jd
@@ -16,6 +16,7 @@
       <li><a href="#Required">Required notification contents</a></li>
       <li><a href="#Optional">Optional notification contents and settings</a></li>
       <li><a href="#Actions">Notification actions</a></li>
+      <li><a href="#Priority">Notification priority</a></li>
       <li><a href="#SimpleNotification">Creating a simple notification</a></li>
       <li><a href="#ApplyStyle">Applying a big view style to a notification</a></li>
       <li><a href="#Compatibility">Handling compatibility</a></li>
@@ -290,6 +291,26 @@
     {@link android.support.v4.app.NotificationCompat.Builder}.
 </p>
 <!-- ------------------------------------------------------------------------------------------ -->
+<h3 id="Priority">Notification priority</h3>
+<p>
+    If you wish, you can set the priority of a notification. The priority acts
+    as a hint to the device UI about how the notification should be displayed.
+    To set a notification's priority, call {@link
+    android.support.v4.app.NotificationCompat.Builder#setPriority(int)
+    NotificationCompat.Builder.setPriority()} and pass in one of the {@link
+    android.support.v4.app.NotificationCompat} priority constants. There are
+    five priority levels, ranging from {@link
+    android.support.v4.app.NotificationCompat#PRIORITY_MIN} (-2) to {@link
+    android.support.v4.app.NotificationCompat#PRIORITY_MAX} (2); if not set, the
+    priority defaults to {@link
+    android.support.v4.app.NotificationCompat#PRIORITY_DEFAULT} (0).
+</p>
+<p> For information about setting an appropriate priority level, see "Correctly
+    set and manage notification priority" in the <a
+    href="{@docRoot}design/patterns/notifications.html">Notifications</a> Design
+    guide.
+</p>
+<!-- ------------------------------------------------------------------------------------------ -->
 <h3 id="SimpleNotification">Creating a simple notification</h3>
 <p>
     The following snippet illustrates a simple notification that specifies an activity to open when
diff --git a/docs/html/wear/images/notif_summary_framed.png b/docs/html/wear/images/notif_summary_framed.png
new file mode 100644
index 0000000..17b1703
--- /dev/null
+++ b/docs/html/wear/images/notif_summary_framed.png
Binary files differ
diff --git a/docs/html/wear/notifications/stacks.jd b/docs/html/wear/notifications/stacks.jd
index 7f955f6..a2d34ce 100644
--- a/docs/html/wear/notifications/stacks.jd
+++ b/docs/html/wear/notifications/stacks.jd
@@ -2,8 +2,8 @@
 
 @jd:body
 
-<img src="{@docRoot}wear/images/11_bundles_B.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
-<img src="{@docRoot}wear/images/11_bundles_A.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
+<img src="{@docRoot}wear/images/11_bundles_B.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" alt="" />
+<img src="{@docRoot}wear/images/11_bundles_A.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" alt="" />
 
 <p>When creating notifications for a handheld device, you should always aggregate similar
 notifications into a single summary notification. For example, if your app creates notifications
@@ -29,20 +29,44 @@
 
 <p>To create a stack, call <a
 href="{@docRoot}reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#setGroup(java.lang.String, int)">
-<code>setGroup()</code></a> for each notification you want in the stack, passing the same
-group key. For example:</p>
+<code>setGroup()</code></a> for each notification you want in the stack and specify a
+group key. Then call <a href="{@docRoot}reference/android/preview/support/v4/app/NotificationManagerCompat.html#notify(int, android.app.Notification)"><code>notify()</code></a> to send it to the wearable.</p>
 
 <pre style="clear:right">
 final static String GROUP_KEY_EMAILS = "group_key_emails";
 
+// Build the notification and pass this builder to WearableNotifications.Builder
 NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
-         .setContentTitle("New mail from " + sender)
-         .setContentText(subject)
+         .setContentTitle("New mail from " + sender1)
+         .setContentText(subject1)
          .setSmallIcon(R.drawable.new_mail);
 
-Notification notif = new WearableNotifications.Builder(builder)
+Notification notif1 = new WearableNotifications.Builder(builder)
          .setGroup(GROUP_KEY_EMAILS)
          .build();
+
+// Issue the notification
+NotificationManagerCompat notificationManager =
+        NotificationManagerCompat.from(this);
+notificationManager.notify(notificationId1, notif);
+</pre>
+
+<p>Later on, when you create another notification, specify
+the same group key. When you call <a href="{@docRoot}reference/android/preview/support/v4/app/NotificationManagerCompat.html#notify(int, android.app.Notification)"><code>notify()</code></a>, this notification appears
+in the same stack as the previous notification, instead of as a new card:</p>
+
+<pre style="clear:right">
+builder = new NotificationCompat.Builder(mContext)
+         .setContentTitle("New mail from " + sender2)
+         .setContentText(subject2)
+         .setSmallIcon(R.drawable.new_mail);
+
+// Use the same group as the previous notification
+Notification notif2 = new WearableNotifications.Builder(builder)
+         .setGroup(GROUP_KEY_EMAILS)
+         .build();
+
+notificationManager.notify(notificationId2, notif);
 </pre>
 
 <p>By default, notifications appear in the order in which you added them, with the most recent
@@ -54,19 +78,55 @@
 
 <h2 id="AddSummary">Add a Summary Notification</h2>
 
+<img src="{@docRoot}wear/images/notif_summary_framed.png" height="242" width="330" style="float:right;margin:0 0 20px 40px" alt="" />
+
 <p>It's important that you still provide a summary notification that appears on handheld devices.
 So in addition to adding each unique notification to the same stack group, also add a summary
 notification, but set its order position to be <a
 href="{@docRoot}reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY"><code>GROUP_ORDER_SUMMARY</code></a>.</p>
 
-<pre>
-Notification summaryNotification = new WearableNotifications.Builder(builder)
-         .setGroup(GROUP_KEY_EMAILS, WearableNotifications.GROUP_ORDER_SUMMARY)
-         .build();
+<p>This notification does not appear in your stack of notifications on the wearable, but
+appears as the only notification on the handheld device.</p>
+
+<pre style="clear:right">
+Bitmap largeIcon = BitmapFactory.decodeResource(getResources(),
+        R.drawable.ic_large_icon);
+
+builder = new NotificationCompat.Builder(this)
+        .setSmallIcon(R.drawable.ic_small_icon)
+        .setLargeIcon(largeIcon);
+
+// Use the same group key and pass this builder to InboxStyle notification
+WearableNotifications.Builder wearableBuilder = new WearableNotifications
+        .Builder(builder)
+        .setGroup(GROUP_KEY_EMAILS,
+                WearableNotifications.GROUP_ORDER_SUMMARY);
+
+// Build the final notification to show on the handset
+Notification summaryNotification = new NotificationCompat.InboxStyle(
+        wearableBuilder.getCompatBuilder())
+        .addLine("Alex Faaborg   Check this out")
+        .addLine("Jeff Chang   Launch Party")
+        .setBigContentTitle("2 new messages")
+        .setSummaryText("johndoe@gmail.com")
+        .build();
+
+notificationManager.notify(notificationId3, summaryNotification);
 </pre>
 
-<p>This notification will not appear in your stack of notifications on the wearable, but
-appears as the only notification on the handheld device.
+<p>
+This notification uses {@link android.support.v4.app.NotificationCompat.InboxStyle},
+which gives you an easy way to create notifications for email or messaging apps.
+You can use this style, another one defined in {@link android.support.v4.app.NotificationCompat},
+or no style for the summary notification.
+</p>
 
+<p class="note"><b>Tip:</b>
+To style the text like in the example screenshot, see
+<a href="{@docRoot}guide/topics/resources/string-resource.html#StylingWithHTML">Styling
+with HTML markup</a> and
+<a href="{@docRoot}guide/topics/resources/string-resource.html#StylingWithSpannables">Styling
+with Spannables</a>.
+</p>
 </body>
-</html>
+</html>
\ No newline at end of file
diff --git a/graphics/java/android/graphics/CanvasProperty.java b/graphics/java/android/graphics/CanvasProperty.java
index 99ea9b1..be86060 100644
--- a/graphics/java/android/graphics/CanvasProperty.java
+++ b/graphics/java/android/graphics/CanvasProperty.java
@@ -16,12 +16,15 @@
 
 package android.graphics;
 
+import com.android.internal.util.VirtualRefBasePtr;
+
 /**
  * TODO: Make public?
  * @hide
  */
 public final class CanvasProperty<T> {
-    private long mNativeContainer;
+
+    private VirtualRefBasePtr mProperty;
 
     public static CanvasProperty<Float> createFloat(float initialValue) {
         return new CanvasProperty<Float>(nCreateFloat(initialValue));
@@ -32,25 +35,14 @@
     }
 
     private CanvasProperty(long nativeContainer) {
-        mNativeContainer = nativeContainer;
+        mProperty = new VirtualRefBasePtr(nativeContainer);
     }
 
     /** @hide */
     public long getNativeContainer() {
-        return mNativeContainer;
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            nUnref(mNativeContainer);
-            mNativeContainer = 0;
-        } finally {
-            super.finalize();
-        }
+        return mProperty.get();
     }
 
     private static native long nCreateFloat(float initialValue);
     private static native long nCreatePaint(long initialValuePaintPtr);
-    private static native void nUnref(long ptr);
 }
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index 0b074cc..86fc7c3 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -17,13 +17,13 @@
 #define ANIMATOR_H
 
 #include <cutils/compiler.h>
+#include <utils/RefBase.h>
 #include <utils/StrongPointer.h>
 
 #include "CanvasProperty.h"
 #include "Interpolator.h"
 #include "TreeInfo.h"
 #include "utils/Macros.h"
-#include "utils/VirtualLightRefBase.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/CanvasProperty.h b/libs/hwui/CanvasProperty.h
index 2e1d176..6074394 100644
--- a/libs/hwui/CanvasProperty.h
+++ b/libs/hwui/CanvasProperty.h
@@ -16,8 +16,9 @@
 #ifndef CANVASPROPERTY_H
 #define CANVASPROPERTY_H
 
+#include <utils/RefBase.h>
+
 #include "utils/Macros.h"
-#include "utils/VirtualLightRefBase.h"
 
 #include <SkPaint.h>
 
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index eaeb772..b2ead5b 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -41,7 +41,6 @@
 #include "Matrix.h"
 #include "DeferredDisplayList.h"
 #include "RenderProperties.h"
-#include "utils/VirtualLightRefBase.h"
 
 class SkBitmap;
 class SkPaint;
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 159903c..bc62ee1 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -45,7 +45,6 @@
 #include "DisplayList.h"
 #include "RenderProperties.h"
 #include "TreeInfo.h"
-#include "utils/VirtualLightRefBase.h"
 
 class SkBitmap;
 class SkPaint;
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index d22cb8a..08e9a1a 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -214,18 +214,28 @@
     int dstY = y + glyph->mBitmapTop;
 
     CacheTexture* cacheTexture = glyph->mCacheTexture;
-
-    uint32_t cacheWidth = cacheTexture->getWidth();
-    uint32_t startY = glyph->mStartY * cacheWidth;
-    uint32_t endY = startY + (glyph->mBitmapHeight * cacheWidth);
-
     PixelBuffer* pixelBuffer = cacheTexture->getPixelBuffer();
+
+    uint32_t formatSize = PixelBuffer::formatSize(pixelBuffer->getFormat());
+    uint32_t cacheWidth = cacheTexture->getWidth();
+    uint32_t srcStride = formatSize * cacheWidth;
+    uint32_t startY = glyph->mStartY * srcStride;
+    uint32_t endY = startY + (glyph->mBitmapHeight * srcStride);
+
     const uint8_t* cacheBuffer = pixelBuffer->map();
 
     for (uint32_t cacheY = startY, bitmapY = dstY * bitmapWidth; cacheY < endY;
-            cacheY += cacheWidth, bitmapY += bitmapWidth) {
-        memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth);
+            cacheY += srcStride, bitmapY += bitmapWidth) {
+
+        if (formatSize == 1) {
+            memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth);
+        } else {
+            for (uint32_t i = 0; i < glyph->mBitmapWidth; ++i) {
+                bitmap[bitmapY + dstX + i] = cacheBuffer[cacheY + (glyph->mStartX + i)*formatSize];
+            }
+        }
     }
+
 }
 
 void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
diff --git a/libs/hwui/utils/VirtualLightRefBase.h b/libs/hwui/utils/VirtualLightRefBase.h
deleted file mode 100644
index b545aab..0000000
--- a/libs/hwui/utils/VirtualLightRefBase.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef VIRTUALLIGHTREFBASE_H
-#define VIRTUALLIGHTREFBASE_H
-
-#include <utils/RefBase.h>
-
-namespace android {
-namespace uirenderer {
-
-// This is a wrapper around LightRefBase that simply enforces a virtual
-// destructor to eliminate the template requirement of LightRefBase
-class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
-public:
-    virtual ~VirtualLightRefBase() {}
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif /* VIRTUALLIGHTREFBASE_H */
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index b2b2bd8..9069a55 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -436,6 +436,8 @@
         @Override
         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
             mode.getMenuInflater().inflate(R.menu.mode_directory, menu);
+            mode.setTitle(getResources()
+                    .getString(R.string.mode_selected_count, mCurrentView.getCheckedItemCount()));
             return true;
         }
 
diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml
index 546ddd4..0d943ed 100644
--- a/packages/Keyguard/res/layout/keyguard_status_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_status_view.xml
@@ -28,7 +28,7 @@
     androidprv:layout_maxWidth="@dimen/keyguard_security_width"
     androidprv:layout_maxHeight="@dimen/keyguard_security_height"
     android:gravity="center_horizontal|top"
-    android:layout_marginTop="32dp"
+    android:layout_marginTop="48dp"
     android:layout_marginBottom="32dp"
     android:contentDescription="@string/keyguard_accessibility_status">
     <LinearLayout
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png
deleted file mode 100644
index 3b1944d..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png
deleted file mode 100644
index 693abf5..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png
deleted file mode 100644
index 807f607..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png
deleted file mode 100644
index 15340d3..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png
deleted file mode 100644
index e562bc2..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png
deleted file mode 100644
index e3cc9b0..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_quicksettings_normal.png
deleted file mode 100644
index a2e8fe1..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_quicksettings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png
deleted file mode 100644
index e15981a..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_notify_quicksettings.xml b/packages/SystemUI/res/drawable/ic_notify_quicksettings.xml
deleted file mode 100644
index 7cf3175..0000000
--- a/packages/SystemUI/res/drawable/ic_notify_quicksettings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true"
-         android:drawable="@drawable/ic_notify_quicksettings_normal" />
-    <item
-         android:drawable="@drawable/ic_notify_quicksettings_normal" />
-</selector>
-
diff --git a/packages/SystemUI/res/drawable/ic_notify_settings.xml b/packages/SystemUI/res/drawable/ic_notify_settings.xml
deleted file mode 100644
index 9303ca4..0000000
--- a/packages/SystemUI/res/drawable/ic_notify_settings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true"
-         android:drawable="@drawable/ic_notify_settings_normal" />
-    <item
-         android:drawable="@drawable/ic_notify_settings_normal" />
-</selector>
-
diff --git a/packages/SystemUI/res/drawable/ic_settings_24dp.xml b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
new file mode 100644
index 0000000..5c38a22
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
@@ -0,0 +1,29 @@
+<!-- Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+<size
+android:width="24dp"
+android:height="24dp"/>
+
+    <viewport android:viewportWidth="24.0"
+          android:viewportHeight="24.0"/>
+
+<group>
+<path
+     android:pathData="M19.4,13.0c0.0,-0.3 0.1,-0.6 0.1,-1.0s0.0,-0.7 -0.1,-1.0l2.1,-1.7c0.2,-0.2 0.2,-0.4 0.1,-0.6l-2.0,-3.5C19.5,5.1 19.3,5.0 19.0,5.1l-2.5,1.0c-0.5,-0.4 -1.1,-0.7 -1.7,-1.0l-0.4,-2.6C14.5,2.2 14.2,2.0 14.0,2.0l-4.0,0.0C9.8,2.0 9.5,2.2 9.5,2.4L9.1,5.1C8.5,5.3 8.0,5.7 7.4,6.1L5.0,5.1C4.7,5.0 4.5,5.1 4.3,5.3l-2.0,3.5C2.2,8.9 2.3,9.2 2.5,9.4L4.6,11.0c0.0,0.3 -0.1,0.6 -0.1,1.0s0.0,0.7 0.1,1.0l-2.1,1.7c-0.2,0.2 -0.2,0.4 -0.1,0.6l2.0,3.5C4.5,18.9 4.7,19.0 5.0,18.9l2.5,-1.0c0.5,0.4 1.1,0.7 1.7,1.0l0.4,2.6c0.0,0.2 0.2,0.4 0.5,0.4l4.0,0.0c0.2,0.0 0.5,-0.2 0.5,-0.4l0.4,-2.6c0.6,-0.3 1.2,-0.6 1.7,-1.0l2.5,1.0c0.2,0.1 0.5,0.0 0.6,-0.2l2.0,-3.5c0.1,-0.2 0.1,-0.5 -0.1,-0.6L19.4,13.0zM12.0,15.5c-1.9,0.0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5s3.5,1.6 3.5,3.5S13.9,15.5 12.0,15.5z"
+     android:fill="#ffffffff"
+     />
+</group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/notification_header_bg.xml b/packages/SystemUI/res/drawable/notification_header_bg.xml
index c5ba18b..09d0d7d 100644
--- a/packages/SystemUI/res/drawable/notification_header_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_header_bg.xml
@@ -18,13 +18,13 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_pressed="true">
         <shape>
-            <solid android:color="#ff54656e" />
+            <solid android:color="@color/background_color_1_press" />
             <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" />
         </shape>
     </item>
     <item>
         <shape>
-            <solid android:color="#ff374248" />
+            <solid android:color="@color/background_color_1" />
             <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" />
         </shape>
     </item>
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
new file mode 100644
index 0000000..c015cc8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:versionCode="1" >
+
+    <size
+        android:height="16dp"
+        android:width="16dp" />
+
+    <viewport
+        android:viewportHeight="100"
+        android:viewportWidth="100" />
+
+    <group>
+        <path
+            android:name="x"
+            android:pathData="M0,0L100,100M0,100L100,0z"
+            android:stroke="@color/recents_task_bar_dark_dismiss_color"
+            android:strokeWidth="8.0"
+            android:strokeLineCap="square" />
+    </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_light.xml b/packages/SystemUI/res/drawable/recents_dismiss_light.xml
new file mode 100644
index 0000000..9c93db9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_dismiss_light.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:versionCode="1" >
+
+    <size
+        android:height="16dp"
+        android:width="16dp" />
+
+    <viewport
+        android:viewportHeight="100"
+        android:viewportWidth="100" />
+
+    <group>
+        <path
+            android:name="x"
+            android:pathData="M0,0L100,100M0,100L100,0z"
+            android:stroke="@color/recents_task_bar_light_dismiss_color"
+            android:strokeWidth="8.0"
+            android:strokeLineCap="square" />
+    </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/heads_up.xml b/packages/SystemUI/res/layout/heads_up.xml
index e4954e7..7d9cfa1 100644
--- a/packages/SystemUI/res/layout/heads_up.xml
+++ b/packages/SystemUI/res/layout/heads_up.xml
@@ -20,7 +20,7 @@
 <com.android.systemui.statusbar.policy.HeadsUpNotificationView
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_height="wrap_content"
-        android:layout_width="@dimen/notification_panel_width"
+        android:layout_width="match_parent"
         android:id="@+id/content_holder"
         android:background="@drawable/notification_panel_bg"
         />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
index b7df51d..1efda8c 100644
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
@@ -29,7 +29,7 @@
     <com.android.systemui.settings.ToggleSlider
         android:id="@+id/brightness_slider"
         android:layout_width="0dp"
-        android:layout_height="40dp"
+        android:layout_height="44dp"
         android:layout_gravity="center_vertical"
         android:layout_weight="1"
         systemui:text="@string/status_bar_settings_auto_brightness_label" />
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index f7df18eb..bda6431 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -63,6 +63,13 @@
             android:maxLines="2"
             android:ellipsize="marquee"
             android:fadingEdge="horizontal" />
+        <ImageView
+            android:id="@+id/dismiss_task"
+            android:layout_width="@dimen/recents_task_view_application_icon_size"
+            android:layout_height="@dimen/recents_task_view_application_icon_size"
+            android:layout_gravity="center_vertical|end"
+            android:padding="23dp"
+            android:src="@drawable/recents_dismiss_dark" />
     </com.android.systemui.recents.views.TaskBarView>
 </com.android.systemui.recents.views.TaskView>
 
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 7308626..f045da4 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -22,7 +22,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
     android:id="@+id/notification_panel"
-    android:layout_width="0dp"
+    android:layout_width="match_parent"
     android:layout_height="match_parent"
     >
 
@@ -34,15 +34,6 @@
         android:layout_gravity="bottom"
         />
 
-    <com.android.keyguard.CarrierText
-        android:id="@+id/keyguard_carrier_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="2dp"
-        android:layout_marginLeft="8dp"
-        android:ellipsize="marquee"
-        android:textAppearance="?android:attr/textAppearanceMedium" />
-
     <include
         layout="@layout/keyguard_status_view"
         android:layout_height="wrap_content"
@@ -59,9 +50,8 @@
         />
 
     <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
+        style="@style/NotificationsQuickSettings"
         android:id="@+id/notification_container_parent"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
         android:clipToPadding="false"
         android:clipChildren="false">
 
@@ -98,13 +88,7 @@
 
     </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
 
-
-    <include layout="@layout/status_bar_expanded_header"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/status_bar_header_height"
-        android:layout_marginLeft="@dimen/notification_side_padding"
-        android:layout_marginRight="@dimen/notification_side_padding"
-        />
+    <include layout="@layout/status_bar_expanded_header" />
 
     <include
         layout="@layout/keyguard_bottom_area"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 3759f82..89fa988 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -20,12 +20,12 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
     android:id="@+id/header"
-    android:layout_width="match_parent"
+    style="@style/StatusBarHeader"
     android:layout_height="@dimen/status_bar_header_height"
-    android:orientation="horizontal"
-    android:gravity="center_vertical"
+    android:paddingStart="@dimen/notification_side_padding"
+    android:paddingEnd="@dimen/notification_side_padding"
     android:baselineAligned="false"
-    android:elevation="14dp"
+    android:elevation="10dp"
     >
 
     <View
@@ -38,10 +38,12 @@
     <RelativeLayout
         android:id="@+id/datetime"
         android:layout_width="wrap_content"
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
         android:layout_gravity="start"
-        android:paddingStart="8dp"
-        android:paddingEnd="8dp"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
         android:background="@drawable/ic_notify_button_bg"
         android:enabled="false"
         >
@@ -49,10 +51,9 @@
             android:id="@+id/clock"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginEnd="8dp"
             android:singleLine="true"
             android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
-            android:layout_centerVertical="true"
+            systemui:amPmStyle="normal"
             />
 
         <com.android.systemui.statusbar.policy.DateView android:id="@+id/date"
@@ -60,17 +61,49 @@
             android:layout_height="wrap_content"
             android:singleLine="true"
             android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
-            android:layout_toEndOf="@id/clock"
-            android:layout_alignBaseline="@id/clock"
+            android:layout_below="@id/clock"
             />
     </RelativeLayout>
 
+    <com.android.keyguard.CarrierText
+        android:id="@+id/keyguard_carrier_text"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/status_bar_header_height_keyguard"
+        android:layout_marginLeft="8dp"
+        android:gravity="center_vertical"
+        android:ellipsize="marquee"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
+        android:layout_width="40dp"
+        android:layout_height="@dimen/status_bar_header_height"
+        android:layout_alignParentEnd="true"
+        android:background="@null"
+        android:scaleType="centerInside"
+        android:padding="6dp"
+        />
+
+    <ImageButton android:id="@+id/settings_button"
+        style="@android:style/Widget.Quantum.Button.Borderless"
+        android:layout_toStartOf="@id/multi_user_switch"
+        android:layout_width="56dp"
+        android:layout_height="@dimen/status_bar_header_height"
+        android:src="@drawable/ic_settings_24dp"
+        android:contentDescription="@string/accessibility_desc_quick_settings"/>
+
     <FrameLayout android:id="@+id/system_icons_container"
         android:layout_width="wrap_content"
         android:layout_height="@dimen/status_bar_header_height"
-        android:layout_alignParentEnd="true"
-        android:layout_marginEnd="16dp"
+        android:layout_toStartOf="@id/multi_user_switch"
+        android:layout_marginEnd="4dp"
         />
+
+    <include
+        layout="@layout/quick_settings_brightness_dialog"
+        android:id="@+id/brightness_container"
+        android:layout_width="match_parent"
+        />
+
     <TextView
         android:id="@+id/header_debug_info"
         android:visibility="invisible"
diff --git a/packages/SystemUI/res/layout/status_bar_flip_button.xml b/packages/SystemUI/res/layout/status_bar_flip_button.xml
deleted file mode 100644
index f4d7033..0000000
--- a/packages/SystemUI/res/layout/status_bar_flip_button.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2014 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
-  -->
-
-<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/settings_button"
-    android:layout_width="50dp"
-    android:layout_height="50dp"
-    android:scaleType="center"
-    android:src="@drawable/ic_notify_quicksettings"
-    android:background="@drawable/ic_notify_button_bg"
-    android:contentDescription="@string/accessibility_desc_quick_settings"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
index e6d7c93..0e84762 100644
--- a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
+++ b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
@@ -28,6 +28,7 @@
         android:layout_alignParentTop="true"
         android:layout_alignParentBottom="true"
         android:button="@null"
+        android:background="@*android:drawable/switch_track_quantum"
         />
     <com.android.systemui.settings.ToggleSeekBar
         android:id="@+id/slider"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index e95f3c3..26616cd 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -35,9 +35,8 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent" >
         <include layout="@layout/status_bar_expanded"
-            android:layout_width="@dimen/notification_panel_width"
+            android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:layout_gravity="start|top"
             android:visibility="gone" />
     </com.android.systemui.statusbar.phone.PanelHolder>
 
diff --git a/packages/SystemUI/res/layout/user_switcher_host.xml b/packages/SystemUI/res/layout/user_switcher_host.xml
index bc56cf6..70c5042 100644
--- a/packages/SystemUI/res/layout/user_switcher_host.xml
+++ b/packages/SystemUI/res/layout/user_switcher_host.xml
@@ -22,7 +22,8 @@
         xmlns:tools="http://schemas.android.com/tools"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="#dd000000">
+        android:background="#dd000000"
+        android:elevation="12dp">
     <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index b7becac..d4a99866 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -18,4 +18,15 @@
     <style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer">
         <item name="android:layout_width">480dp</item>
     </style>
+
+    <style name="NotificationsQuickSettings">
+        <item name="android:layout_width">@dimen/notification_panel_width</item>
+        <item name="android:layout_height">match_parent</item>
+        <item name="android:layout_gravity">top|center_horizontal</item>
+    </style>
+
+    <style name="StatusBarHeader">
+        <item name="android:layout_width">@dimen/notification_panel_width</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index f5674d2..8fd1206 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -51,6 +51,13 @@
             <enum name="end" value="1" />
         </attr>
     </declare-styleable>
+    <declare-styleable name="Clock">
+        <attr name="amPmStyle" format="enum">
+            <enum name="normal" value="0" />
+            <enum name="small" value="1" />
+            <enum name="gone" value="2" />
+        </attr>
+    </declare-styleable>
     <attr name="orientation">
         <enum name="horizontal" value="0" />
         <enum name="vertical" value="1" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index c78ff8e..c1a4e26 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -52,10 +52,18 @@
     <!-- The default recents task bar background color. -->
     <color name="recents_task_bar_default_background_color">#e6444444</color>
     <!-- The default recents task bar text color. -->
-    <color name="recents_task_bar_default_text_color">#ffffffff</color>
+    <color name="recents_task_bar_default_text_color">#ffeeeeee</color>
     <!-- The recents task bar light text color to be drawn on top of dark backgrounds. -->
-    <color name="recents_task_bar_light_text_color">#ffffffff</color>
+    <color name="recents_task_bar_light_text_color">#ffeeeeee</color>
     <!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
     <color name="recents_task_bar_dark_text_color">#ff222222</color>
+    <!-- The recents task bar light dismiss icon color to be drawn on top of dark backgrounds. -->
+    <color name="recents_task_bar_light_dismiss_color">#ffeeeeee</color>
+    <!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
+    <color name="recents_task_bar_dark_dismiss_color">#ff333333</color>
 
+    <!-- Our quantum color palette (deep teal) -->
+    <color name="primary_color">#ff7fcac3</color>
+    <color name="background_color_1">#ff384248</color>
+    <color name="background_color_1_press">#ff54656e</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index c0376f0..21eb41c 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -119,7 +119,7 @@
     <!-- The animation duration for animating in the info pane. -->
     <integer name="recents_animate_task_view_info_pane_duration">150</integer>
     <!-- The animation duration for animating the removal of a task view. -->
-    <integer name="recents_animate_task_view_remove_duration">150</integer>
+    <integer name="recents_animate_task_view_remove_duration">250</integer>
     <!-- The minimum alpha for the dim applied to cards that go deeper into the stack. -->
     <integer name="recents_max_task_stack_view_dim">96</integer>
     <!-- Transposes the search bar layout in landscape -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 50df706..ab34030 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -158,6 +158,9 @@
     <!-- Height of the status bar header bar when expanded -->
     <dimen name="status_bar_header_height_expanded">144dp</dimen>
 
+    <!-- Height of the status bar header bar when on Keyguard -->
+    <dimen name="status_bar_header_height_keyguard">40dp</dimen>
+
     <!-- Gravity for the notification panel -->
     <!-- 0x37 = fill_horizontal|top -->
     <integer name="notification_panel_layout_gravity">0x37</integer>
@@ -196,9 +199,6 @@
     <!-- Quick Settings CA Cert Warning tile geometry: gap between icon and text -->
     <dimen name="qs_cawarn_tile_margin_below_icon">3dp</dimen>
 
-    <!-- The width of the notification panel window: match_parent below sw600dp -->
-    <dimen name="notification_panel_width">-1dp</dimen>
-
     <!-- used by DessertCase -->
     <dimen name="dessert_case_cell_size">192dp</dimen>
 
@@ -224,7 +224,7 @@
     <dimen name="recents_task_view_z_increment">5dp</dimen>
 
     <!-- The amount to translate when animating the removal of a task. -->
-    <dimen name="recents_task_view_remove_anim_translation_x">75dp</dimen>
+    <dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
 
     <!-- The amount of space a user has to scroll to dismiss any info panes. -->
     <dimen name="recents_task_stack_scroll_dismiss_info_pane_distance">50dp</dimen>
@@ -243,7 +243,10 @@
     <dimen name="top_stack_peek_amount">12dp</dimen>
 
     <!-- Space reserved for the cards behind the top card in the bottom stack -->
-    <dimen name="bottom_stack_peek_amount">18dp</dimen>
+    <dimen name="bottom_stack_peek_amount">12dp</dimen>
+
+    <!-- The height of the area before the bottom stack in which the notifications slow down -->
+    <dimen name="bottom_stack_slow_down_length">12dp</dimen>
 
     <!-- The side padding of the notifications-->
     <dimen name="notification_side_padding">8dp</dimen>
@@ -251,8 +254,11 @@
     <!-- Z distance between notifications if they are in the stack -->
     <dimen name="z_distance_between_notifications">2dp</dimen>
 
+    <!-- The padding between the individual notification cards when dimmed. -->
+    <dimen name="notification_padding_dimmed">0dp</dimen>
+
     <!-- The padding between the individual notification cards. -->
-    <dimen name="notification_padding">3dp</dimen>
+    <dimen name="notification_padding">4dp</dimen>
 
     <!-- The total height of the stack in its collapsed size (i.e. when quick settings is open) -->
     <dimen name="collapsed_stack_height">94dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 8ab646d..4f52870 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -69,8 +69,7 @@
     <style name="TextAppearance.StatusBar.Expanded" parent="@*android:style/TextAppearance.StatusBar" />
 
     <style name="TextAppearance.StatusBar.Expanded.Clock">
-        <item name="android:textSize">32dp</item>
-        <item name="android:fontFamily">sans-serif-light</item>
+        <item name="android:textSize">18dp</item>
         <item name="android:textStyle">normal</item>
         <item name="android:textColor">#ffffff</item>
     </style>
@@ -78,8 +77,7 @@
     <style name="TextAppearance.StatusBar.Expanded.Date">
         <item name="android:textSize">12dp</item>
         <item name="android:textStyle">normal</item>
-        <item name="android:textColor">#cccccc</item>
-        <item name="android:textAllCaps">true</item>
+        <item name="android:textColor">#afb3b6</item>
     </style>
 
     <style name="TextAppearance.StatusBar.Expanded.Network" parent="@style/TextAppearance.StatusBar.Expanded.Date">
@@ -138,6 +136,7 @@
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
         <item name="android:padding">16dp</item>
+        <item name="android:layout_alignParentBottom">true</item>
     </style>
 
     <style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer" />
@@ -169,6 +168,16 @@
         <item name="android:textSize">14dp</item>
     </style>
 
-    <style name="systemui_theme" parent="@android:style/Theme.DeviceDefault" />
+    <style name="systemui_theme" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:colorPrimary">@color/primary_color</item>
+    </style>
 
+    <style name="NotificationsQuickSettings">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">match_parent</item>
+    </style>
+
+    <style name="StatusBarHeader">
+        <item name="android:layout_width">match_parent</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 3ef8316..19a1b11 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -255,15 +255,10 @@
     /** Loads the first task thumbnail */
     Bitmap loadFirstTaskThumbnail() {
         SystemServicesProxy ssp = mSystemServicesProxy;
-        List<ActivityManager.RecentTaskInfo> tasks = ssp.getRecentTasks(1,
-                UserHandle.CURRENT.getIdentifier());
-        for (ActivityManager.RecentTaskInfo t : tasks) {
-            // Skip tasks in the home stack
-            if (ssp.isInHomeStack(t.persistentId)) {
-                return null;
-            }
+        List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1);
 
-            return ssp.getTaskThumbnail(t.persistentId);
+        for (ActivityManager.RunningTaskInfo t : tasks) {
+            return ssp.getTaskThumbnail(t.id);
         }
         return null;
     }
@@ -286,17 +281,6 @@
         return (tasks.size() > 1);
     }
 
-    /** Returns whether the base intent of the top task stack was launched with the flag
-     * Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS. */
-    boolean isTopTaskExcludeFromRecents(List<ActivityManager.RecentTaskInfo> tasks) {
-        if (tasks.size() > 0) {
-            ActivityManager.RecentTaskInfo t = tasks.get(0);
-            Console.log(t.baseIntent.toString());
-            return (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
-        }
-        return false;
-    }
-
     /** Converts from the device rotation to the degree */
     float getDegreesForRotation(int value) {
         switch (value) {
@@ -416,16 +400,14 @@
         }
 
         // Otherwise, Recents is not the front-most activity and we should animate into it.  If
-        // the activity at the root of the top task stack is excluded from recents, or if that
-        // task stack is in the home stack, then we just do a simple transition.  Otherwise, we
-        // animate to the rects defined by the Recents service, which can differ depending on the
-        // number of items in the list.
+        // the activity at the root of the top task stack in the home stack, then we just do a
+        // simple transition.  Otherwise, we animate to the rects defined by the Recents service,
+        // which can differ depending on the number of items in the list.
         List<ActivityManager.RecentTaskInfo> recentTasks =
-                ssp.getRecentTasks(4, UserHandle.CURRENT.getIdentifier());
+                ssp.getRecentTasks(2, UserHandle.CURRENT.getIdentifier());
         Rect taskRect = hasMultipleRecentsTask(recentTasks) ? mMultipleCountFirstTaskRect :
                 mSingleCountFirstTaskRect;
-        boolean isTaskExcludedFromRecents = isTopTaskExcludeFromRecents(recentTasks);
-        boolean useThumbnailTransition = !isTopTaskHome && !isTaskExcludedFromRecents &&
+        boolean useThumbnailTransition = !isTopTaskHome &&
                 hasValidTaskRects();
 
         if (useThumbnailTransition) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java b/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java
deleted file mode 100644
index 95ab8e8..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.android.systemui.recents;
-
-import android.animation.TimeInterpolator;
-
-/**
- * A pre-baked bezier-curved interpolator for quantum-paper transitions.
- */
-public class BakedBezierInterpolator implements TimeInterpolator {
-    public static final BakedBezierInterpolator INSTANCE = new BakedBezierInterpolator();
-
-    /**
-     * Use the INSTANCE variable instead of instantiating.
-     */
-    private BakedBezierInterpolator() {
-        super();
-    }
-
-    /**
-     * Lookup table values.
-     * Generated using a Bezier curve from (0,0) to (1,1) with control points:
-     * P0 (0,0)
-     * P1 (0.4, 0)
-     * P2 (0.2, 1.0)
-     * P3 (1.0, 1.0)
-     *
-     * Values sampled with x at regular intervals between 0 and 1.
-     */
-    private static final float[] VALUES = new float[] {
-        0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f,
-        0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f, 0.1186f,
-        0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f, 0.3386f,
-        0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f, 0.5933f,
-        0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f, 0.7502f, 0.763f,
-        0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f, 0.8588f, 0.8672f,
-        0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f, 0.9232f, 0.9281f, 0.9328f,
-        0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f, 0.9632f, 0.9662f, 0.9695f, 0.9722f,
-        0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f, 0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f,
-        0.9944f, 0.9955f, 0.9964f, 0.9973f, 0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f
-    };
-
-    private static final float STEP_SIZE = 1.0f / (VALUES.length - 1);
-
-    @Override
-    public float getInterpolation(float input) {
-        if (input >= 1.0f) {
-            return 1.0f;
-        }
-
-        if (input <= 0f) {
-            return 0f;
-        }
-
-        int position = Math.min(
-                (int)(input * (VALUES.length - 1)),
-                VALUES.length - 2);
-
-        float quantized = position * STEP_SIZE;
-        float difference = input - quantized;
-        float weight = difference / STEP_SIZE;
-
-        return VALUES[position] + weight * (VALUES[position + 1] - VALUES[position]);
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 1d6a76c..90998da 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -32,7 +32,7 @@
             // Enables the use of theme colors as the task bar background
             public static final boolean EnableTaskBarThemeColors = true;
             // Enables the info pane on long-press
-            public static final boolean EnableInfoPane = true;
+            public static final boolean EnableInfoPane = false;
             // Enables the search bar layout
             public static final boolean EnableSearchLayout = true;
             // Enables the dynamic shadows behind each task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 463cf74..9afc1cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -23,6 +23,8 @@
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import com.android.systemui.R;
 
 
@@ -42,6 +44,8 @@
 
     public float animationPxMovementPerSecond;
 
+    public Interpolator defaultBezierInterpolator;
+
     public int filteringCurrentViewsMinAnimDuration;
     public int filteringNewViewsMinAnimDuration;
     public int taskBarEnterAnimDuration;
@@ -121,7 +125,6 @@
                 res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment);
         searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
 
-
         taskBarViewDefaultBackgroundColor =
                 res.getColor(R.color.recents_task_bar_default_background_color);
         taskBarViewDefaultTextColor =
@@ -131,6 +134,9 @@
         taskBarViewDarkTextColor =
                 res.getColor(R.color.recents_task_bar_dark_text_color);
 
+        defaultBezierInterpolator = AnimationUtils.loadInterpolator(context,
+                        com.android.internal.R.interpolator.fast_out_slow_in);
+
         // Update the search widget id
         SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
         searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index c64ca54..1ca0476 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -35,7 +35,6 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
@@ -362,7 +361,7 @@
         return mSystemServicesProxy;
     }
 
-    private List<ActivityManager.RecentTaskInfo> getRecentTasks(Context context) {
+    private List<ActivityManager.RecentTaskInfo> getRecentTasks() {
         long t1 = System.currentTimeMillis();
 
         SystemServicesProxy ssp = mSystemServicesProxy;
@@ -375,23 +374,6 @@
         Console.log(Constants.Log.App.TaskDataLoader,
                 "[RecentsTaskLoader|tasks]", "" + tasks.size());
 
-        // Remove home/recents tasks
-        Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
-        while (iter.hasNext()) {
-            ActivityManager.RecentTaskInfo t = iter.next();
-
-            // Skip tasks in the home stack
-            if (ssp.isInHomeStack(t.persistentId)) {
-                iter.remove();
-                continue;
-            }
-            // Skip tasks from this Recents package
-            if (t.baseIntent.getComponent().getPackageName().equals(context.getPackageName())) {
-                iter.remove();
-                continue;
-            }
-        }
-
         return tasks;
     }
 
@@ -408,7 +390,7 @@
 
         // Get the recent tasks
         SystemServicesProxy ssp = mSystemServicesProxy;
-        List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(context);
+        List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks();
 
         // Add each task to the task stack
         t1 = System.currentTimeMillis();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
index 0d3ee38..8d82883 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
@@ -30,7 +30,6 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -40,6 +39,7 @@
 import android.util.Pair;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Random;
 
@@ -54,7 +54,7 @@
     IPackageManager mIpm;
     UserManager mUm;
     SearchManager mSm;
-    String mPackage;
+    String mRecentsPackage;
     ComponentName mAssistComponent;
 
     Bitmap mDummyIcon;
@@ -67,7 +67,7 @@
         mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mIpm = AppGlobals.getPackageManager();
         mSm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
-        mPackage = context.getPackageName();
+        mRecentsPackage = context.getPackageName();
 
         // Resolve the assist intent
         Intent assist = mSm.getAssistIntent(context, false);
@@ -83,14 +83,14 @@
     }
 
     /** Returns a list of the recents tasks */
-    public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
+    public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId) {
         if (mAm == null) return null;
 
         // If we are mocking, then create some recent tasks
         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
             ArrayList<ActivityManager.RecentTaskInfo> tasks =
                     new ArrayList<ActivityManager.RecentTaskInfo>();
-            int count = Math.min(numTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount);
+            int count = Math.min(numLatestTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount);
             for (int i = 0; i < count; i++) {
                 // Create a dummy component name
                 int packageIndex = i % Constants.DebugFlags.App.SystemServicesProxyMockPackageCount;
@@ -114,9 +114,43 @@
             return tasks;
         }
 
-        return mAm.getRecentTasksForUser(numTasks,
+        // Remove home/recents/excluded tasks
+        int minNumTasksToQuery = 10;
+        int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
+        List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery,
                 ActivityManager.RECENT_IGNORE_UNAVAILABLE |
-                        ActivityManager.RECENT_INCLUDE_PROFILES, userId);
+                ActivityManager.RECENT_INCLUDE_PROFILES |
+                ActivityManager.RECENT_WITH_EXCLUDED, userId);
+        boolean isFirstValidTask = true;
+        Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
+        while (iter.hasNext()) {
+            ActivityManager.RecentTaskInfo t = iter.next();
+
+            // NOTE: The order of these checks happens in the expected order of the traversal of the
+            // tasks
+
+            // Skip tasks from this Recents package
+            if (t.baseIntent.getComponent().getPackageName().equals(mRecentsPackage)) {
+                iter.remove();
+                continue;
+            }
+            // Check the first non-recents task, include this task even if it is marked as excluded
+            // from recents.  In other words, only remove excluded tasks if it is not the first task
+            boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                    == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+            if (isExcluded && !isFirstValidTask) {
+                iter.remove();
+                continue;
+            }
+            isFirstValidTask = false;
+            // Skip tasks in the home stack
+            if (isInHomeStack(t.persistentId)) {
+                iter.remove();
+                continue;
+            }
+        }
+
+        return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
     }
 
     /** Returns a list of the running tasks */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
index b602f84..46e6ee9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Color;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 
 /* Common code */
 public class Utilities {
@@ -54,12 +55,15 @@
                 0.0722f * Color.blue(color));
     }
 
-    /** Returns the ideal text color to draw on top of a specified background color. */
-    public static int getIdealTextColorForBackgroundColor(int color) {
-        RecentsConfiguration configuration = RecentsConfiguration.getInstance();
+    /** Returns the ideal color to draw on top of a specified background color. */
+    public static int getIdealColorForBackgroundColor(int color, int lightRes, int darkRes) {
         int greyscale = colorToGreyscale(color);
-        return (greyscale < 128) ? configuration.taskBarViewLightTextColor :
-                configuration.taskBarViewDarkTextColor;
-
+        return (greyscale < 128) ? lightRes : darkRes;
+    }
+    /** Returns the ideal drawable to draw on top of a specified background color. */
+    public static Drawable getIdealResourceForBackgroundColor(int color, Drawable lightRes,
+                                                           Drawable darkRes) {
+        int greyscale = colorToGreyscale(color);
+        return (greyscale < 128) ? lightRes : darkRes;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index c6cb812..07caa1b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -16,7 +16,10 @@
 
 package com.android.systemui.recents.views;
 
+import android.animation.ValueAnimator;
 import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -32,9 +35,13 @@
 class TaskBarView extends FrameLayout {
     Task mTask;
 
+    ImageView mDismissButton;
     ImageView mApplicationIcon;
     TextView mActivityDescription;
 
+    Drawable mLightDismissDrawable;
+    Drawable mDarkDismissDrawable;
+
     public TaskBarView(Context context) {
         this(context, null);
     }
@@ -49,6 +56,9 @@
 
     public TaskBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        Resources res = context.getResources();
+        mLightDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_light);
+        mDarkDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_dark);
     }
 
     @Override
@@ -56,6 +66,28 @@
         // Initialize the icon and description views
         mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
         mActivityDescription = (TextView) findViewById(R.id.activity_description);
+        mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
+    }
+
+    /** Synchronizes this bar view's properties with the task's transform */
+    void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
+                                             TaskViewTransform toTransform, int duration) {
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+        if (duration > 0) {
+            if (animateFromTransform != null) {
+                mDismissButton.setAlpha(animateFromTransform.dismissAlpha);
+            }
+            mDismissButton.animate()
+                    .alpha(toTransform.dismissAlpha)
+                    .setStartDelay(0)
+                    .setDuration(duration)
+                    .setInterpolator(config.defaultBezierInterpolator)
+                    .withLayer()
+                    .start();
+        } else {
+            mDismissButton.setAlpha(toTransform.dismissAlpha);
+        }
+        mDismissButton.invalidate();
     }
 
     /** Binds the bar view to the task */
@@ -74,7 +106,10 @@
         int tint = t.colorPrimary;
         if (Constants.DebugFlags.App.EnableTaskBarThemeColors && tint != 0) {
             setBackgroundColor(tint);
-            mActivityDescription.setTextColor(Utilities.getIdealTextColorForBackgroundColor(tint));
+            mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint,
+                    configuration.taskBarViewLightTextColor, configuration.taskBarViewDarkTextColor));
+            mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint,
+                    mLightDismissDrawable, mDarkDismissDrawable));
         } else {
             setBackgroundColor(configuration.taskBarViewDefaultBackgroundColor);
             mActivityDescription.setTextColor(configuration.taskBarViewDefaultTextColor);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
index c6c29a6..f1c362a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
@@ -30,7 +30,6 @@
 import android.widget.Button;
 import android.widget.FrameLayout;
 import com.android.systemui.R;
-import com.android.systemui.recents.BakedBezierInterpolator;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.Utilities;
@@ -111,7 +110,8 @@
         int duration = Utilities.calculateTranslationAnimationDuration((int) mMaxClipRadius);
         mCircularClipAnimator = ObjectAnimator.ofFloat(this, "circularClipRadius", toRadius);
         mCircularClipAnimator.setDuration(duration);
-        mCircularClipAnimator.setInterpolator(BakedBezierInterpolator.INSTANCE);
+        mCircularClipAnimator.setInterpolator(
+                RecentsConfiguration.getInstance().defaultBezierInterpolator);
         mCircularClipAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -143,7 +143,7 @@
                 .scaleX(1f)
                 .scaleY(1f)
                 .setDuration(duration)
-                .setInterpolator(BakedBezierInterpolator.INSTANCE)
+                .setInterpolator(RecentsConfiguration.getInstance().defaultBezierInterpolator)
                 .withLayer()
                 .start();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 55c38a9..b64225e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -37,7 +37,6 @@
 import android.widget.FrameLayout;
 import android.widget.OverScroller;
 import com.android.systemui.R;
-import com.android.systemui.recents.BakedBezierInterpolator;
 import com.android.systemui.recents.Console;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
@@ -170,6 +169,9 @@
             transform.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
         }
 
+        // Set the alphas
+        transform.dismissAlpha = Math.max(-1f, Math.min(0f, t)) + 1f;
+
         // Update the rect and visibility
         transform.rect.set(mTaskRect);
         if (t < -(numPeekCards + 1)) {
@@ -338,7 +340,7 @@
         mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll);
         mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
                 curScroll, 250));
-        mScrollAnimator.setInterpolator(BakedBezierInterpolator.INSTANCE);
+        mScrollAnimator.setInterpolator(RecentsConfiguration.getInstance().defaultBezierInterpolator);
         mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
@@ -1036,6 +1038,15 @@
         }
     }
 
+    @Override
+    public void onTaskDismissed(TaskView tv) {
+        Task task = tv.getTask();
+        // Remove the task from the view
+        mStack.removeTask(task);
+        // Notify the callback that we've removed the task and it can clean up after it
+        mCb.onTaskRemoved(task);
+    }
+
     /**** View.OnClickListener Implementation ****/
 
     @Override
@@ -1095,6 +1106,7 @@
 
     @Override
     public void onComponentRemoved(Set<ComponentName> cns) {
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
         // For other tasks, just remove them directly if they no longer exist
         ArrayList<Task> tasks = mStack.getTasks();
         for (int i = tasks.size() - 1; i >= 0; i--) {
@@ -1477,13 +1489,7 @@
     @Override
     public void onChildDismissed(View v) {
         TaskView tv = (TaskView) v;
-        Task task = tv.getTask();
-
-        // Remove the task from the view
-        mSv.mStack.removeTask(task);
-
-        // Notify the callback that we've removed the task and it can clean up after it
-        mSv.mCb.onTaskRemoved(task);
+        mSv.onTaskDismissed(tv);
 
         // Disable HW layers
         mSv.decHwLayersRefCount("swipeComplete");
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index b03f389..5fad629 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -31,7 +31,6 @@
 import android.view.animation.AccelerateInterpolator;
 import android.widget.FrameLayout;
 import com.android.systemui.R;
-import com.android.systemui.recents.BakedBezierInterpolator;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.model.Task;
@@ -46,6 +45,7 @@
         public void onTaskInfoPanelShown(TaskView tv);
         public void onTaskInfoPanelHidden(TaskView tv);
         public void onTaskAppInfoClicked(TaskView tv);
+        public void onTaskDismissed(TaskView tv);
 
         // public void onTaskViewReboundToTask(TaskView tv, Task t);
     }
@@ -143,6 +143,10 @@
         int minZ = config.taskViewTranslationZMinPx;
         int incZ = config.taskViewTranslationZIncrementPx;
 
+        // Update the bar view
+        mBarView.updateViewPropertiesToTaskTransform(animateFromTransform, toTransform, duration);
+
+        // Update this task view
         if (duration > 0) {
             if (animateFromTransform != null) {
                 setTranslationY(animateFromTransform.translationY);
@@ -161,7 +165,7 @@
                     .scaleY(toTransform.scale)
                     .alpha(toTransform.alpha)
                     .setDuration(duration)
-                    .setInterpolator(BakedBezierInterpolator.INSTANCE)
+                    .setInterpolator(config.defaultBezierInterpolator)
                     .withLayer()
                     .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                         @Override
@@ -221,8 +225,8 @@
         mBarView.setAlpha(0f);
         mBarView.animate()
                 .alpha(1f)
-                .setStartDelay(235)
-                .setInterpolator(BakedBezierInterpolator.INSTANCE)
+                .setStartDelay(250)
+                .setInterpolator(config.defaultBezierInterpolator)
                 .setDuration(config.taskBarEnterAnimDuration)
                 .withLayer()
                 .start();
@@ -234,7 +238,7 @@
         mBarView.animate()
             .alpha(0f)
             .setStartDelay(0)
-            .setInterpolator(BakedBezierInterpolator.INSTANCE)
+            .setInterpolator(config.defaultBezierInterpolator)
             .setDuration(config.taskBarExitAnimDuration)
             .withLayer()
             .withEndAction(new Runnable() {
@@ -252,7 +256,7 @@
         animate().translationX(config.taskViewRemoveAnimTranslationXPx)
             .alpha(0f)
             .setStartDelay(0)
-            .setInterpolator(BakedBezierInterpolator.INSTANCE)
+            .setInterpolator(config.defaultBezierInterpolator)
             .setDuration(config.taskViewRemoveAnimDuration)
             .withLayer()
             .withEndAction(new Runnable() {
@@ -310,7 +314,7 @@
         mInfoView.animate()
                 .alpha(0f)
                 .setDuration(config.taskViewInfoPaneAnimDuration)
-                .setInterpolator(BakedBezierInterpolator.INSTANCE)
+                .setInterpolator(config.defaultBezierInterpolator)
                 .withLayer()
                 .withEndAction(new Runnable() {
                     @Override
@@ -380,6 +384,7 @@
             mInfoView.rebindToTask(mTask, reloadingTaskData);
             // Rebind any listeners
             mBarView.mApplicationIcon.setOnClickListener(this);
+            mBarView.mDismissButton.setOnClickListener(this);
             mInfoView.mAppInfoButton.setOnClickListener(this);
         }
         mTaskDataLoaded = true;
@@ -405,6 +410,15 @@
             hideInfoPane();
         } else if (v == mBarView.mApplicationIcon) {
             mCb.onTaskIconClicked(this);
+        } else if (v == mBarView.mDismissButton) {
+            // Animate out the view and call the callback
+            final TaskView tv = this;
+            animateRemoval(new Runnable() {
+                @Override
+                public void run() {
+                    mCb.onTaskDismissed(tv);
+                }
+            });
         } else if (v == mInfoView.mAppInfoButton) {
             mCb.onTaskAppInfoClicked(this);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index 0748bbb..e6391a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -24,6 +24,7 @@
     public int translationY = 0;
     public float scale = 1f;
     public float alpha = 1f;
+    public float dismissAlpha = 1f;
     public boolean visible = false;
     public Rect rect = new Rect();
     float t;
@@ -36,6 +37,7 @@
         translationY = o.translationY;
         scale = o.scale;
         alpha = o.alpha;
+        dismissAlpha = o.dismissAlpha;
         visible = o.visible;
         rect.set(o.rect);
         t = o.t;
@@ -44,6 +46,6 @@
     @Override
     public String toString() {
         return "TaskViewTransform y: " + translationY + " scale: " + scale + " alpha: " + alpha +
-                " visible: " + visible + " rect: " + rect;
+                " visible: " + visible + " rect: " + rect + " dismissAlpha: " + dismissAlpha;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 327e715..1747e6e 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -119,7 +119,6 @@
             }
         };
         mBrightnessObserver = new BrightnessObserver(mHandler);
-        mBrightnessObserver.startObserving();
 
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mMinimumBacklight = pm.getMinimumScreenBrightnessSetting();
@@ -128,13 +127,6 @@
         mAutomaticAvailable = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_automatic_brightness_available);
         mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power"));
-
-        // Update the slider and mode before attaching the listener so we don't receive the
-        // onChanged notifications for the initial values.
-        updateMode();
-        updateSlider();
-
-        control.setOnChangedListener(this);
     }
 
     public void addStateChangedCallback(BrightnessStateChangeCallback cb) {
@@ -150,11 +142,24 @@
         // Do nothing
     }
 
+    public void registerCallbacks() {
+        mBrightnessObserver.startObserving();
+        mUserTracker.startTracking();
+
+        // Update the slider and mode before attaching the listener so we don't receive the
+        // onChanged notifications for the initial values.
+        updateMode();
+        updateSlider();
+
+        mControl.setOnChangedListener(this);
+    }
+
     /** Unregister all call backs, both to and from the controller */
     public void unregisterCallbacks() {
         mBrightnessObserver.stopObserving();
         mChangeCallbacks.clear();
         mUserTracker.stopTracking();
+        mControl.setOnChangedListener(null);
     }
 
     public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index bd5e5e8..27881c4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -92,6 +92,7 @@
         mBrightnessController = new BrightnessController(getContext(),
                 (ImageView) findViewById(R.id.brightness_icon),
                 (ToggleSlider) findViewById(R.id.brightness_slider));
+        mBrightnessController.registerCallbacks();
         dismissBrightnessDialog(mBrightnessDialogLongTimeout);
         mBrightnessController.addStateChangedCallback(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
index 036bd4f..f8ff616 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
@@ -29,9 +29,6 @@
     private int mCurrentUserId;
 
     public CurrentUserTracker(Context context) {
-        IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
-        context.registerReceiver(this, filter);
-        mCurrentUserId = ActivityManager.getCurrentUser();
         mContext = context;
     }
 
@@ -50,6 +47,12 @@
         }
     }
 
+    public void startTracking() {
+        mCurrentUserId = ActivityManager.getCurrentUser();
+        IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
+        mContext.registerReceiver(this, filter);
+    }
+
     public void stopTracking() {
         mContext.unregisterReceiver(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
index 7d38058..4b78072 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
@@ -62,7 +62,6 @@
 
         mToggle = (CompoundButton) findViewById(R.id.toggle);
         mToggle.setOnCheckedChangeListener(mCheckListener);
-        mToggle.setBackground(res.getDrawable(R.drawable.status_bar_toggle_button));
 
         mSlider = (SeekBar) findViewById(R.id.slider);
         mSlider.setOnSeekBarChangeListener(mSeekListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
new file mode 100644
index 0000000..c26f15e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.ContactsContract;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+
+import com.android.systemui.R;
+import com.android.systemui.settings.UserSwitcherHostView;
+import com.android.systemui.statusbar.policy.UserInfoController;
+
+/**
+ * Image button for the multi user switcher.
+ */
+public class MultiUserSwitch extends ImageButton implements View.OnClickListener,
+        UserInfoController.OnUserInfoChangedListener {
+
+    private ViewGroup mOverlayParent;
+
+    public MultiUserSwitch(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        setOnClickListener(this);
+    }
+
+    public void setOverlayParent(ViewGroup parent) {
+        mOverlayParent = parent;
+    }
+
+    @Override
+    public void onClick(View v) {
+        final UserManager um = UserManager.get(getContext());
+        if (um.isUserSwitcherEnabled()) {
+            final UserSwitcherHostView switcher =
+                    (UserSwitcherHostView) LayoutInflater.from(getContext()).inflate(
+                            R.layout.user_switcher_host, mOverlayParent, false);
+            switcher.setFinishRunnable(new Runnable() {
+                @Override
+                public void run() {
+                    mOverlayParent.removeView(switcher);
+                }
+            });
+            switcher.refreshUsers();
+            mOverlayParent.addView(switcher);
+        } else {
+            Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
+                    getContext(), v, ContactsContract.Profile.CONTENT_URI,
+                    ContactsContract.QuickContact.MODE_LARGE, null);
+            getContext().startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+        }
+    }
+
+    public void setUserInfoController(UserInfoController userInfoController) {
+        userInfoController.addListener(this);
+    }
+
+    @Override
+    public void onUserInfoChanged(String name, Drawable picture) {
+        setImageDrawable(picture);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c74911f..8387e36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -95,6 +95,7 @@
         super.onFinishInflate();
         mHeader = (StatusBarHeaderView) findViewById(R.id.header);
         mHeader.getBackgroundView().setOnClickListener(this);
+        mHeader.setOverlayParent(this);
         mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
         mStackScrollerContainer = findViewById(R.id.notification_container_parent);
         mQsContainer = (QuickSettingsContainerView) findViewById(R.id.quick_settings_container);
@@ -413,11 +414,8 @@
         if (!mQsExpansionEnabled) {
             return false;
         }
-        View headerView = mStatusBar.getBarState() == StatusBarState.KEYGUARD && !mQsExpanded
-                ? mKeyguardStatusView
-                : mHeader;
-        boolean onHeader = x >= headerView.getLeft() && x <= headerView.getRight()
-                && y >= headerView.getTop() && y <= headerView.getBottom();
+        boolean onHeader = x >= mHeader.getLeft() && x <= mHeader.getRight()
+                && y >= mHeader.getTop() && y <= mHeader.getBottom();
         if (mQsExpanded) {
             return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0);
         } else {
@@ -449,6 +447,7 @@
         if (toHeader) {
             mHeader.attachSystemIcons(systemIcons);
         } else {
+            mHeader.onSystemIconsDetached();
             mStatusBar.reattachSystemIcons();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index a9a504e..4d09d6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -111,6 +111,7 @@
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
 import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
@@ -184,6 +185,7 @@
     LocationController mLocationController;
     NetworkController mNetworkController;
     RotationLockController mRotationLockController;
+    UserInfoController mUserInfoController;
 
     int mNaturalBarHeight = -1;
     int mIconSize = -1;
@@ -221,7 +223,6 @@
     int mNotificationPanelGravity;
     int mNotificationPanelMarginBottomPx;
     float mNotificationPanelMinHeightFrac;
-    boolean mNotificationPanelIsFullScreenWidth;
     TextView mNotificationPanelDebugText;
 
     // settings
@@ -247,7 +248,6 @@
     private int mCarrierLabelHeight;
     private TextView mEmergencyCallLabel;
     private int mStatusBarHeaderHeight;
-    private View mKeyguardCarrierLabel;
 
     private boolean mShowCarrierInPanel = false;
 
@@ -552,8 +552,6 @@
         mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
                 R.id.notification_panel);
         mNotificationPanel.setStatusBar(this);
-        mNotificationPanelIsFullScreenWidth =
-            (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
 
         // make the header non-responsive to clicks
         mNotificationPanel.findViewById(R.id.header).setOnTouchListener(
@@ -626,13 +624,13 @@
                 (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
                         R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
         mKeyguardIconOverflowContainer.setOnActivatedListener(this);
-        mKeyguardCarrierLabel = mStatusBarWindow.findViewById(R.id.keyguard_carrier_text);
         mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
         mStackScroller.addView(mKeyguardIconOverflowContainer);
 
         mExpandedContents = mStackScroller;
 
         mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
+        mHeader.setActivityStarter(this);
         mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
         mKeyguardBottomArea =
                 (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
@@ -647,11 +645,9 @@
             mDateTimeView.setEnabled(true);
         }
 
-        if (!mNotificationPanelIsFullScreenWidth) {
-            mNotificationPanel.setSystemUiVisibility(
-                    View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS |
-                    View.STATUS_BAR_DISABLE_CLOCK);
-        }
+        mNotificationPanel.setSystemUiVisibility(
+                View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS |
+                View.STATUS_BAR_DISABLE_CLOCK);
 
         mTicker = new MyTicker(context, mStatusBarView);
 
@@ -672,6 +668,7 @@
                 || QuickSettings.DEBUG_GONE_TILES) {
             mRotationLockController = new RotationLockController(mContext);
         }
+        mUserInfoController = new UserInfoController(mContext);
         final SignalClusterView signalCluster =
                 (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
 
@@ -726,11 +723,6 @@
         mFlipSettingsView = mSettingsContainer;
         if (mSettingsContainer != null) {
             mQS = new QuickSettings(mContext, mSettingsContainer);
-            if (!mNotificationPanelIsFullScreenWidth) {
-                mSettingsContainer.setSystemUiVisibility(
-                        View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS
-                        | View.STATUS_BAR_DISABLE_SYSTEM_INFO);
-            }
             mQS.setService(this);
             mQS.setBar(mStatusBarView);
             mQS.setup(mNetworkController, mBluetoothController, mBatteryController,
@@ -739,6 +731,10 @@
             mQS = null; // fly away, be free
         }
 
+        // User info. Trigger first load.
+        mHeader.setUserInfoController(mUserInfoController);
+        mUserInfoController.reloadUserInfo();
+
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mBroadcastReceiver.onReceive(mContext,
                 new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
@@ -1864,13 +1860,7 @@
 
     private void checkBarModes() {
         if (mDemoMode) return;
-        int sbMode = mStatusBarMode;
-        if (panelsEnabled() && (mInteractingWindows & StatusBarManager.WINDOW_STATUS_BAR) != 0
-                && mState != StatusBarState.KEYGUARD) {
-            // if panels are expandable, force the status bar opaque on any interaction
-            sbMode = MODE_OPAQUE;
-        }
-        checkBarMode(sbMode, mStatusBarWindowState, mStatusBarView.getBarTransitions());
+        checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions());
         if (mNavigationBarView != null) {
             checkBarMode(mNavigationBarMode,
                     mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
@@ -2717,14 +2707,12 @@
             mKeyguardBottomArea.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
-            mKeyguardCarrierLabel.setVisibility(View.VISIBLE);
 
             mNotificationPanel.closeQs();
         } else {
             mKeyguardStatusView.setVisibility(View.GONE);
             mKeyguardBottomArea.setVisibility(View.GONE);
             mKeyguardIndicationTextView.setVisibility(View.GONE);
-            mKeyguardCarrierLabel.setVisibility(View.GONE);
         }
         mSettingsContainer.setKeyguardShowing(mState == StatusBarState.KEYGUARD);
         mHeader.setKeyguardShowing(mState == StatusBarState.KEYGUARD);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index e6de057..084bfcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -63,7 +63,7 @@
     }
 
     @Override
-    public void onAttachedToWindow() {
+    public void onFinishInflate() {
         mBarTransitions.init();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index e1ef83a..005b0d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -460,6 +460,7 @@
                 rebindMediaRouterAsCurrentUser();
             }
         };
+        mUserTracker.startTracking();
 
         mNextAlarmObserver = new NextAlarmObserver(mHandler);
         mNextAlarmObserver.startObserving();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 6b3c94e..1af9a6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -17,26 +17,47 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.content.Intent;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 
 import com.android.systemui.R;
+import com.android.systemui.settings.BrightnessController;
+import com.android.systemui.settings.ToggleSlider;
+import com.android.systemui.statusbar.policy.UserInfoController;
 
 /**
  * The view to manage the header area in the expanded status bar.
  */
-public class StatusBarHeaderView extends RelativeLayout {
+public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener {
 
     private boolean mExpanded;
+    private boolean mKeyguardShowing;
+
     private View mBackground;
     private ViewGroup mSystemIconsContainer;
     private View mDateTime;
+    private View mKeyguardCarrierText;
+    private MultiUserSwitch mMultiUserSwitch;
+    private View mDate;
+    private View mStatusIcons;
+    private View mSignalCluster;
+    private View mSettingsButton;
+    private View mBrightnessContainer;
 
     private int mCollapsedHeight;
     private int mExpandedHeight;
+    private int mKeyguardHeight;
+
+    private int mKeyguardWidth = ViewGroup.LayoutParams.MATCH_PARENT;
+    private int mNormalWidth;
+
+    private ActivityStarter mActivityStarter;
+    private BrightnessController mBrightnessController;
 
     public StatusBarHeaderView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -48,6 +69,15 @@
         mBackground = findViewById(R.id.background);
         mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container);
         mDateTime = findViewById(R.id.datetime);
+        mKeyguardCarrierText = findViewById(R.id.keyguard_carrier_text);
+        mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
+        mDate = findViewById(R.id.date);
+        mSettingsButton = findViewById(R.id.settings_button);
+        mSettingsButton.setOnClickListener(this);
+        mBrightnessContainer = findViewById(R.id.brightness_container);
+        mBrightnessController = new BrightnessController(getContext(),
+                (ImageView) findViewById(R.id.brightness_icon),
+                (ToggleSlider) findViewById(R.id.brightness_slider));
         loadDimens();
     }
 
@@ -55,10 +85,17 @@
         mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_header_height);
         mExpandedHeight = getResources().getDimensionPixelSize(
                 R.dimen.status_bar_header_height_expanded);
+        mKeyguardHeight = getResources().getDimensionPixelSize(
+                R.dimen.status_bar_header_height_keyguard);
+        mNormalWidth = getLayoutParams().width;
+    }
+
+    public void setActivityStarter(ActivityStarter activityStarter) {
+        mActivityStarter = activityStarter;
     }
 
     public int getCollapsedHeight() {
-        return mCollapsedHeight;
+        return mKeyguardShowing ? mKeyguardHeight : mCollapsedHeight;
     }
 
     public int getExpandedHeight() {
@@ -66,11 +103,81 @@
     }
 
     public void setExpanded(boolean expanded) {
-        if (expanded != mExpanded) {
-            ViewGroup.LayoutParams lp = getLayoutParams();
-            lp.height = expanded ? mExpandedHeight : mCollapsedHeight;
+        boolean changed = expanded != mExpanded;
+        mExpanded = expanded;
+        if (changed) {
+            updateHeights();
+            updateVisibilities();
+            updateSystemIconsLayoutParams();
+            updateBrightnessControllerState();
+        }
+    }
+
+    private void updateHeights() {
+        boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
+        int height;
+        if (mExpanded) {
+            height = mExpandedHeight;
+        } else if (onKeyguardAndCollapsed) {
+            height = mKeyguardHeight;
+        } else {
+            height = mCollapsedHeight;
+        }
+        ViewGroup.LayoutParams lp = getLayoutParams();
+        if (lp.height != height) {
+            lp.height = height;
             setLayoutParams(lp);
-            mExpanded = expanded;
+        }
+        int systemIconsContainerHeight = onKeyguardAndCollapsed ? mKeyguardHeight : mCollapsedHeight;
+        lp = mSystemIconsContainer.getLayoutParams();
+        if (lp.height != systemIconsContainerHeight) {
+            lp.height = systemIconsContainerHeight;
+            mSystemIconsContainer.setLayoutParams(lp);
+        }
+        lp = mMultiUserSwitch.getLayoutParams();
+        if (lp.height != systemIconsContainerHeight) {
+            lp.height = systemIconsContainerHeight;
+            mMultiUserSwitch.setLayoutParams(lp);
+        }
+    }
+
+    private void updateWidth() {
+        int width = mKeyguardShowing ? mKeyguardWidth : mNormalWidth;
+        ViewGroup.LayoutParams lp = getLayoutParams();
+        if (width != lp.width) {
+            lp.width = width;
+            setLayoutParams(lp);
+        }
+    }
+
+    private void updateVisibilities() {
+        boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
+        mBackground.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
+        mDateTime.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
+        mKeyguardCarrierText.setVisibility(onKeyguardAndCollapsed ? View.VISIBLE : View.GONE);
+        mDate.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
+        mSettingsButton.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
+        mBrightnessContainer.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
+        if (mStatusIcons != null) {
+            mStatusIcons.setVisibility(!mExpanded ? View.VISIBLE : View.GONE);
+        }
+        if (mSignalCluster != null) {
+            mSignalCluster.setVisibility(!mExpanded ? View.VISIBLE : View.GONE);
+        }
+    }
+
+    private void updateSystemIconsLayoutParams() {
+        RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsContainer.getLayoutParams();
+        lp.addRule(RelativeLayout.START_OF, mExpanded
+                ? mSettingsButton.getId()
+                : mMultiUserSwitch.getId());
+    }
+
+    private void updateBrightnessControllerState() {
+        if (mExpanded) {
+            mBrightnessController.registerCallbacks();
+        } else {
+            mBrightnessController.unregisterCallbacks();
         }
     }
 
@@ -94,15 +201,49 @@
 
     public void attachSystemIcons(LinearLayout systemIcons) {
         mSystemIconsContainer.addView(systemIcons);
+        mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
+        mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
+    }
+
+    public void onSystemIconsDetached() {
+        if (mStatusIcons != null) {
+            mStatusIcons.setVisibility(View.VISIBLE);
+        }
+        if (mSignalCluster != null) {
+            mSignalCluster.setVisibility(View.VISIBLE);
+        }
+        mStatusIcons = null;
+        mSignalCluster = null;
     }
 
     public void setKeyguardShowing(boolean keyguardShowing) {
-        mBackground.setVisibility(keyguardShowing ? View.INVISIBLE : View.VISIBLE);
-        mDateTime.setVisibility(keyguardShowing ? View.INVISIBLE : View.VISIBLE);
+        mKeyguardShowing = keyguardShowing;
         if (keyguardShowing) {
             setZ(0);
         } else {
             setTranslationZ(0);
         }
+        updateHeights();
+        updateWidth();
+        updateVisibilities();
+    }
+
+    public void setUserInfoController(UserInfoController userInfoController) {
+        mMultiUserSwitch.setUserInfoController(userInfoController);
+    }
+
+    public void setOverlayParent(ViewGroup parent) {
+        mMultiUserSwitch.setOverlayParent(parent);
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v == mSettingsButton) {
+            startSettingsActivity();
+        }
+    }
+
+    private void startSettingsActivity() {
+        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index a4c9df5..8809d18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -78,9 +78,8 @@
                         | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
                         | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
                 PixelFormat.TRANSLUCENT);
-
         mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-        mLp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+        mLp.gravity = Gravity.TOP;
         mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
         mLp.setTitle("StatusBar");
         mLp.packageName = mContext.getPackageName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 8ced1c9..55a0bba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.TypedArray;
 import android.os.Bundle;
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
@@ -30,6 +31,7 @@
 import android.widget.TextView;
 
 import com.android.systemui.DemoMode;
+import com.android.systemui.R;
 
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
@@ -52,7 +54,7 @@
     private static final int AM_PM_STYLE_SMALL   = 1;
     private static final int AM_PM_STYLE_GONE    = 2;
 
-    private static final int AM_PM_STYLE = AM_PM_STYLE_GONE;
+    private final int mAmPmStyle;
 
     public Clock(Context context) {
         this(context, null);
@@ -64,6 +66,15 @@
 
     public Clock(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        TypedArray a = context.getTheme().obtainStyledAttributes(
+                attrs,
+                R.styleable.Clock,
+                0, 0);
+        try {
+            mAmPmStyle = a.getInt(R.styleable.Clock_amPmStyle, AM_PM_STYLE_GONE);
+        } finally {
+            a.recycle();
+        }
     }
 
     @Override
@@ -145,7 +156,7 @@
              * add dummy characters around it to let us find it again after
              * formatting and change its size.
              */
-            if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) {
+            if (mAmPmStyle != AM_PM_STYLE_NORMAL) {
                 int a = -1;
                 boolean quoted = false;
                 for (int i = 0; i < format.length(); i++) {
@@ -177,15 +188,15 @@
         }
         String result = sdf.format(mCalendar.getTime());
 
-        if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) {
+        if (mAmPmStyle != AM_PM_STYLE_NORMAL) {
             int magic1 = result.indexOf(MAGIC1);
             int magic2 = result.indexOf(MAGIC2);
             if (magic1 >= 0 && magic2 > magic1) {
                 SpannableStringBuilder formatted = new SpannableStringBuilder(result);
-                if (AM_PM_STYLE == AM_PM_STYLE_GONE) {
+                if (mAmPmStyle == AM_PM_STYLE_GONE) {
                     formatted.delete(magic1, magic2+1);
                 } else {
-                    if (AM_PM_STYLE == AM_PM_STYLE_SMALL) {
+                    if (mAmPmStyle == AM_PM_STYLE_SMALL) {
                         CharacterStyle style = new RelativeSizeSpan(0.7f);
                         formatted.setSpan(style, magic1, magic2,
                                           Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
new file mode 100644
index 0000000..173af40
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar.policy;
+
+import android.app.ActivityManagerNative;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.ContactsContract;
+import android.security.KeyChain;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.view.RotationPolicy;
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public final class UserInfoController {
+
+    private static final String TAG = "UserInfoController";
+
+    private final Context mContext;
+    private final ArrayList<OnUserInfoChangedListener> mCallbacks =
+            new ArrayList<OnUserInfoChangedListener>();
+    private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask;
+
+    private boolean mUseDefaultAvatar;
+    private String mUserName;
+    private Drawable mUserDrawable;
+
+    public UserInfoController(Context context) {
+        mContext = context;
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
+        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+        mContext.registerReceiver(mReceiver, filter);
+
+        IntentFilter profileFilter = new IntentFilter();
+        profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED);
+        profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
+        mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter,
+                null, null);
+    }
+
+    public void addListener(OnUserInfoChangedListener callback) {
+        mCallbacks.add(callback);
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                reloadUserInfo();
+            } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+                if (mUseDefaultAvatar) {
+                    reloadUserInfo();
+                }
+            }
+        }
+    };
+
+    private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) ||
+                    Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
+                try {
+                    final int currentUser = ActivityManagerNative.getDefault().getCurrentUser().id;
+                    final int changedUser =
+                            intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
+                    if (changedUser == currentUser) {
+                        reloadUserInfo();
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Couldn't get current user id for profile change", e);
+                }
+            }
+        }
+    };
+
+    public void reloadUserInfo() {
+        if (mUserInfoTask != null) {
+            mUserInfoTask.cancel(false);
+            mUserInfoTask = null;
+        }
+        queryForUserInformation();
+    }
+
+    private Bitmap circularClip(Bitmap input) {
+        Bitmap output = Bitmap.createBitmap(input.getWidth(),
+                input.getHeight(), Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(output);
+        final Paint paint = new Paint();
+        paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
+        paint.setAntiAlias(true);
+        canvas.drawCircle(input.getWidth() / 2, input.getHeight() / 2, input.getWidth() / 2, paint);
+        return output;
+    }
+
+    private void queryForUserInformation() {
+        Context currentUserContext;
+        UserInfo userInfo;
+        try {
+            userInfo = ActivityManagerNative.getDefault().getCurrentUser();
+            currentUserContext = mContext.createPackageContextAsUser("android", 0,
+                    new UserHandle(userInfo.id));
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Couldn't create user context", e);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't get user info", e);
+            throw new RuntimeException(e);
+        }
+        final int userId = userInfo.id;
+        final String userName = userInfo.name;
+
+        final Context context = currentUserContext;
+        mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() {
+            @Override
+            protected Pair<String, Drawable> doInBackground(Void... params) {
+                final UserManager um = UserManager.get(mContext);
+
+                // Fall back to the UserManager nickname if we can't read the name from the local
+                // profile below.
+                String name = userName;
+                Drawable avatar = null;
+                Bitmap rawAvatar = um.getUserIcon(userId);
+                if (rawAvatar != null) {
+                    avatar = new BitmapDrawable(mContext.getResources(), circularClip(rawAvatar));
+                } else {
+                    avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user);
+                    mUseDefaultAvatar = true;
+                }
+
+                // If it's a single-user device, get the profile name, since the nickname is not
+                // usually valid
+                if (um.getUsers().size() <= 1) {
+                    // Try and read the display name from the local profile
+                    final Cursor cursor = context.getContentResolver().query(
+                            ContactsContract.Profile.CONTENT_URI, new String[] {
+                            ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME},
+                            null, null, null);
+                    if (cursor != null) {
+                        try {
+                            if (cursor.moveToFirst()) {
+                                name = cursor.getString(cursor.getColumnIndex(
+                                        ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
+                            }
+                        } finally {
+                            cursor.close();
+                        }
+                    }
+                }
+                return new Pair<String, Drawable>(name, avatar);
+            }
+
+            @Override
+            protected void onPostExecute(Pair<String, Drawable> result) {
+                mUserName = result.first;
+                mUserDrawable = result.second;
+                mUserInfoTask = null;
+                notifyChanged();
+            }
+        };
+        mUserInfoTask.execute();
+    }
+
+    private void notifyChanged() {
+        for (OnUserInfoChangedListener listener : mCallbacks) {
+            listener.onUserInfoChanged(mUserName, mUserDrawable);
+        }
+    }
+
+    public interface OnUserInfoChangedListener {
+        public void onUserInfoChanged(String name, Drawable picture);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index afd5068..27f6619 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -153,7 +153,10 @@
         if (DEBUG) {
             int y = mCollapsedSize;
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-            y = (int) (getLayoutHeight() - mBottomStackPeekSize - mCollapsedSize);
+            y = (int) (getLayoutHeight() - mBottomStackPeekSize
+                    - mStackScrollAlgorithm.getBottomStackSlowDownLength());
+            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+            y = (int) (getLayoutHeight() - mBottomStackPeekSize);
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
             y = (int) getLayoutHeight();
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
@@ -1226,6 +1229,7 @@
      * See {@link AmbientState#setDimmed}.
      */
     public void setDimmed(boolean dimmed, boolean animate) {
+        mStackScrollAlgorithm.setDimmed(dimmed);
         mAmbientState.setDimmed(dimmed);
         if (animate) {
             mDimmedNeedsAnimation = true;
@@ -1311,6 +1315,7 @@
 
                 // ANIMATION_TYPE_DIMMED
                 new AnimationFilter()
+                        .animateY()
                         .animateScale()
                         .animateDimmed()
         };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java
index 38b544f..1c37c35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java
@@ -38,27 +38,26 @@
      *                 the actual visual distance below the top card but is a maximum,
      *                 achieved when the next card just starts transitioning into the stack and
      *                 the stack is full.
-     *                 If totalTransitionDistance is equal to this, we directly start at the peek,
-     *                 otherwise the first element transitions between 0 and
-     *                 totalTransitionDistance - peekSize.
+     *                 If distanceToPeekStart is 0, we directly start at the peek, otherwise the
+     *                 first element transitions between 0 and distanceToPeekStart.
      *                 Visualization:
      *           ---------------------------------------------------   ---
      *          |                                                   |   |
-     *          |                  FIRST ITEM                       |   | <- totalTransitionDistance
+     *          |                  FIRST ITEM                       |   | <- distanceToPeekStart
      *          |                                                   |   |
-     *          |---------------------------------------------------|   |   ---
-     *          |__________________SECOND ITEM______________________|   |    |  <- peekSize
-     *          |===================================================|  _|_  _|_
+     *          |---------------------------------------------------|  ---  ---
+     *          |__________________SECOND ITEM______________________|        |  <- peekSize
+     *          |===================================================|       _|_
      *
-     * @param totalTransitionDistance The total transition distance an element has to go through
+     * @param distanceToPeekStart The distance to the start of the peak.
      * @param linearPart The interpolation factor between the linear and the quadratic amount taken.
      *                   This factor must be somewhere in [0 , 1]
      */
     PiecewiseLinearIndentationFunctor(int maxItemsInStack,
                                       int peekSize,
-                                      int totalTransitionDistance,
+                                      int distanceToPeekStart,
                                       float linearPart) {
-        super(maxItemsInStack, peekSize, totalTransitionDistance);
+        super(maxItemsInStack, peekSize, distanceToPeekStart);
         mBaseValues = new ArrayList<Float>(maxItemsInStack+1);
         initBaseValues();
         mLinearPart = linearPart;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java
index f72947a..034eba6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java
@@ -21,8 +21,8 @@
  */
 public abstract class StackIndentationFunctor {
 
-    protected final int mTotalTransitionDistance;
-    protected final int mDistanceToPeekStart;
+    protected int mTotalTransitionDistance;
+    protected int mDistanceToPeekStart;
     protected int mMaxItemsInStack;
     protected int mPeekSize;
     protected boolean mStackStartsAtPeek;
@@ -37,31 +37,41 @@
      *                 the actual visual distance below the top card but is a maximum,
      *                 achieved when the next card just starts transitioning into the stack and
      *                 the stack is full.
-     *                 If totalTransitionDistance is equal to this, we directly start at the peek,
-     *                 otherwise the first element transitions between 0 and
-     *                 totalTransitionDistance - peekSize.
+     *                 If distanceToPeekStart is 0, we directly start at the peek, otherwise the
+     *                 first element transitions between 0 and distanceToPeekStart.
      *                 Visualization:
      *           ---------------------------------------------------   ---
      *          |                                                   |   |
-     *          |                  FIRST ITEM                       |   | <- totalTransitionDistance
+     *          |                  FIRST ITEM                       |   | <- distanceToPeekStart
      *          |                                                   |   |
-     *          |---------------------------------------------------|   |   ---
-     *          |__________________SECOND ITEM______________________|   |    |  <- peekSize
-     *          |===================================================|  _|_  _|_
+     *          |---------------------------------------------------|  ---  ---
+     *          |__________________SECOND ITEM______________________|        |  <- peekSize
+     *          |===================================================|       _|_
      *
-     * @param totalTransitionDistance The total transition distance an element has to go through
+     * @param distanceToPeekStart The distance to the start of the peak.
      */
-    StackIndentationFunctor(int maxItemsInStack, int peekSize, int totalTransitionDistance) {
-        mTotalTransitionDistance = totalTransitionDistance;
-        mDistanceToPeekStart = mTotalTransitionDistance - peekSize;
+    StackIndentationFunctor(int maxItemsInStack, int peekSize, int distanceToPeekStart) {
+        mDistanceToPeekStart = distanceToPeekStart;
         mStackStartsAtPeek = mDistanceToPeekStart == 0;
         mMaxItemsInStack = maxItemsInStack;
         mPeekSize = peekSize;
+        updateTotalTransitionDistance();
 
     }
 
+    private void updateTotalTransitionDistance() {
+        mTotalTransitionDistance = mDistanceToPeekStart + mPeekSize;
+    }
+
     public void setPeekSize(int mPeekSize) {
         this.mPeekSize = mPeekSize;
+        updateTotalTransitionDistance();
+    }
+
+    public void setDistanceToPeekStart(int distanceToPeekStart) {
+        mDistanceToPeekStart = distanceToPeekStart;
+        mStackStartsAtPeek = mDistanceToPeekStart == 0;
+        updateTotalTransitionDistance();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 5e4d496..bd9de82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -65,13 +65,40 @@
     private ExpandableView mFirstChildWhileExpanding;
     private boolean mExpandedOnStart;
     private int mTopStackTotalSize;
+    private int mPaddingBetweenElementsDimmed;
+    private int mPaddingBetweenElementsNormal;
+    private int mBottomStackSlowDownLength;
 
     public StackScrollAlgorithm(Context context) {
         initConstants(context);
+        updatePadding(false);
+    }
+
+    private void updatePadding(boolean dimmed) {
+        mPaddingBetweenElements = dimmed
+                ? mPaddingBetweenElementsDimmed
+                : mPaddingBetweenElementsNormal;
+        mTopStackTotalSize = mCollapsedSize + mPaddingBetweenElements;
+        mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
+                MAX_ITEMS_IN_TOP_STACK,
+                mTopStackPeekSize,
+                mTopStackTotalSize - mTopStackPeekSize,
+                0.5f);
+        mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
+                MAX_ITEMS_IN_BOTTOM_STACK,
+                mBottomStackPeekSize,
+                getBottomStackSlowDownLength(),
+                0.5f);
+    }
+
+    public int getBottomStackSlowDownLength() {
+        return mBottomStackSlowDownLength + mPaddingBetweenElements;
     }
 
     private void initConstants(Context context) {
-        mPaddingBetweenElements = context.getResources()
+        mPaddingBetweenElementsDimmed = context.getResources()
+                .getDimensionPixelSize(R.dimen.notification_padding_dimmed);
+        mPaddingBetweenElementsNormal = context.getResources()
                 .getDimensionPixelSize(R.dimen.notification_padding);
         mCollapsedSize = context.getResources()
                 .getDimensionPixelSize(R.dimen.notification_min_height);
@@ -82,17 +109,8 @@
         mZDistanceBetweenElements = context.getResources()
                 .getDimensionPixelSize(R.dimen.z_distance_between_notifications);
         mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
-        mTopStackTotalSize = mCollapsedSize + mPaddingBetweenElements;
-        mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
-                MAX_ITEMS_IN_TOP_STACK,
-                mTopStackPeekSize,
-                mTopStackTotalSize,
-                0.5f);
-        mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
-                MAX_ITEMS_IN_BOTTOM_STACK,
-                mBottomStackPeekSize,
-                mCollapsedSize + mBottomStackPeekSize + mPaddingBetweenElements,
-                0.5f);
+        mBottomStackSlowDownLength = context.getResources()
+                .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length);
     }
 
 
@@ -206,7 +224,7 @@
         float bottomPeekStart = mInnerHeight - mBottomStackPeekSize;
 
         // The position where the bottom stack starts.
-        float bottomStackStart = bottomPeekStart - mCollapsedSize;
+        float bottomStackStart = bottomPeekStart - mBottomStackSlowDownLength;
 
         // The y coordinate of the current child.
         float currentYPosition = 0.0f;
@@ -352,7 +370,7 @@
         algorithmState.itemsInBottomStack += algorithmState.partialInBottom;
         childViewState.yTranslation = transitioningPositionStart + offset - childHeight
                 - mPaddingBetweenElements;
-
+        
         // We want at least to be at the end of the top stack when collapsing
         clampPositionToTopStackEnd(childViewState, childHeight);
         childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA;
@@ -621,6 +639,10 @@
         }
     }
 
+    public void setDimmed(boolean dimmed) {
+        updatePadding(dimmed);
+    }
+
     class StackScrollAlgorithmState {
 
         /**
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index 0d1e122..96f9ab0 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -50,6 +50,8 @@
 final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
     private static final boolean LOGD = false;
 
+    private final static boolean VDBG = false;
+
     private final String TAG;
 
     private String mSocket;
@@ -409,7 +411,7 @@
                 loge("timed-out waiting for response to " + logCmd);
                 throw new NativeDaemonFailureException(logCmd, event);
             }
-            log("RMV <- {" + event + "}");
+            if (VDBG) log("RMV <- {" + event + "}");
             events.add(event);
         } while (event.isClassContinue());
 
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 4f0c9b5..512ebc6 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -171,6 +171,7 @@
             return;
         }
         writer.println("Current scorer: " + currentScorer);
+        writer.flush();
 
         for (INetworkScoreCache scoreCache : getScoreCaches()) {
             try {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index ea9de1e..82c13e0 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -360,8 +360,9 @@
     // Lock held on mVibrations
     private void startVibrationLocked(final Vibration vib) {
         try {
-            if (mLowPowerMode && vib.mStreamHint != AudioManager.STREAM_RING)
-                    return;
+            if (mLowPowerMode && vib.mStreamHint != AudioManager.STREAM_RING) {
+                return;
+            }
 
             int mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
                     vib.mStreamHint, vib.mUid, vib.mOpPkg);
@@ -443,7 +444,7 @@
                 }
 
                 mLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
-                     Settings.Global.LOW_POWER_MODE, 0) != 0;
+                         Settings.Global.LOW_POWER_MODE, 0) != 0;
 
                 if (mVibrateInputDevicesSetting) {
                     if (!mInputDeviceListenerRegistered) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 3ae0fd5..3d5fb57 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -518,6 +518,11 @@
                 // Brighten quickly.
                 slow = false;
             }
+            // If low power mode is enabled, brightness level
+            // would be scaled down to half
+            if (mPowerRequest.lowPowerMode) {
+                target = target/2;
+            }
             animateScreenBrightness(clampScreenBrightness(target),
                     slow ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST);
         } else {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 6d2e8592..47a8b2e 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1634,6 +1634,8 @@
 
             mDisplayPowerRequest.blockScreenOn = mScreenOnBlocker.isHeld();
 
+            mDisplayPowerRequest.lowPowerMode = mLowPowerModeEnabled;
+
             mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
                     mRequestWaitForNegativeProximity);
             mRequestWaitForNegativeProximity = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 637beec..836a19c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5038,6 +5038,10 @@
             throw new SecurityException("Requires DISABLE_KEYGUARD permission");
         }
 
+        if (token == null) {
+            throw new IllegalArgumentException("token == null");
+        }
+
         mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
                 KeyguardDisableHandler.KEYGUARD_DISABLE, new Pair<IBinder, String>(token, tag)));
     }
@@ -5049,6 +5053,10 @@
             throw new SecurityException("Requires DISABLE_KEYGUARD permission");
         }
 
+        if (token == null) {
+            throw new IllegalArgumentException("token == null");
+        }
+
         mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
                 KeyguardDisableHandler.KEYGUARD_REENABLE, token));
     }
@@ -5062,6 +5070,11 @@
             != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires DISABLE_KEYGUARD permission");
         }
+
+        if (callback == null) {
+            throw new IllegalArgumentException("callback == null");
+        }
+
         mPolicy.exitKeyguardSecurely(new WindowManagerPolicy.OnKeyguardExitResult() {
             @Override
             public void onKeyguardExitResult(boolean success) {