Merge "MTP: Add support for battery level device property"
diff --git a/api/current.txt b/api/current.txt
index e15c1d7..9df5c9e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -345,7 +345,7 @@
     field public static final int canRetrieveWindowContent = 16843653; // 0x1010385
     field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
     field public static final deprecated int capitalize = 16843113; // 0x1010169
-    field public static final int castsShadow = 16843775; // 0x10103ff
+    field public static final int castsShadow = 16843774; // 0x10103fe
     field public static final int category = 16843752; // 0x10103e8
     field public static final int centerBright = 16842956; // 0x10100cc
     field public static final int centerColor = 16843275; // 0x101020b
@@ -399,10 +399,10 @@
     field public static final int content = 16843355; // 0x101025b
     field public static final int contentAuthority = 16843408; // 0x1010290
     field public static final int contentDescription = 16843379; // 0x1010273
-    field public static final int controlX1 = 16843769; // 0x10103f9
-    field public static final int controlX2 = 16843771; // 0x10103fb
-    field public static final int controlY1 = 16843770; // 0x10103fa
-    field public static final int controlY2 = 16843772; // 0x10103fc
+    field public static final int controlX1 = 16843768; // 0x10103f8
+    field public static final int controlX2 = 16843770; // 0x10103fa
+    field public static final int controlY1 = 16843769; // 0x10103f9
+    field public static final int controlY2 = 16843771; // 0x10103fb
     field public static final int cropToPadding = 16843043; // 0x1010123
     field public static final int cursorVisible = 16843090; // 0x1010152
     field public static final int customNavigationLayout = 16843474; // 0x10102d2
@@ -636,7 +636,6 @@
     field public static final int isScrollContainer = 16843342; // 0x101024e
     field public static final int isSticky = 16843335; // 0x1010247
     field public static final int isolatedProcess = 16843689; // 0x10103a9
-    field public static final int isolatedZVolume = 16843768; // 0x10103f8
     field public static final int itemBackground = 16843056; // 0x1010130
     field public static final int itemIconDisabledAlpha = 16843057; // 0x1010131
     field public static final int itemPadding = 16843565; // 0x101032d
@@ -826,6 +825,7 @@
     field public static final int persistent = 16842765; // 0x101000d
     field public static final int persistentDrawingCache = 16842990; // 0x10100ee
     field public static final deprecated int phoneNumber = 16843111; // 0x1010167
+    field public static final int pinned = 16843776; // 0x1010400
     field public static final int pivotX = 16843189; // 0x10101b5
     field public static final int pivotY = 16843190; // 0x10101b6
     field public static final int popupAnimationStyle = 16843465; // 0x10102c9
@@ -889,7 +889,7 @@
     field public static final int required = 16843406; // 0x101028e
     field public static final int requiredAccountType = 16843734; // 0x10103d6
     field public static final int requiredForAllUsers = 16843728; // 0x10103d0
-    field public static final int requiredForProfile = 16843776; // 0x1010400
+    field public static final int requiredForProfile = 16843775; // 0x10103ff
     field public static final int requiresFadingEdge = 16843685; // 0x10103a5
     field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
     field public static final int resizeMode = 16843619; // 0x1010363
@@ -960,7 +960,7 @@
     field public static final int shadowRadius = 16843108; // 0x1010164
     field public static final int shape = 16843162; // 0x101019a
     field public static final int shareInterpolator = 16843195; // 0x10101bb
-    field public static final int sharedElementName = 16843773; // 0x10103fd
+    field public static final int sharedElementName = 16843772; // 0x10103fc
     field public static final int sharedUserId = 16842763; // 0x101000b
     field public static final int sharedUserLabel = 16843361; // 0x1010261
     field public static final int shouldDisableView = 16843246; // 0x10101ee
@@ -1159,7 +1159,7 @@
     field public static final int transformPivotX = 16843552; // 0x1010320
     field public static final int transformPivotY = 16843553; // 0x1010321
     field public static final int transition = 16843743; // 0x10103df
-    field public static final int transitionGroup = 16843774; // 0x10103fe
+    field public static final int transitionGroup = 16843773; // 0x10103fd
     field public static final int transitionOrdering = 16843744; // 0x10103e0
     field public static final int translationX = 16843554; // 0x1010322
     field public static final int translationY = 16843555; // 0x1010323
@@ -1295,6 +1295,8 @@
     field public static final int dialog_min_width_minor = 17104900; // 0x1050004
     field public static final int notification_large_icon_height = 17104902; // 0x1050006
     field public static final int notification_large_icon_width = 17104901; // 0x1050005
+    field public static final int recents_thumbnail_height = 17104903; // 0x1050007
+    field public static final int recents_thumbnail_width = 17104904; // 0x1050008
     field public static final int thumbnail_height = 17104897; // 0x1050001
     field public static final int thumbnail_width = 17104898; // 0x1050002
   }
@@ -2197,6 +2199,8 @@
     field public static final int Widget_Quantum_Button_Borderless = 16974393; // 0x1030239
     field public static final int Widget_Quantum_Button_Borderless_Small = 16974394; // 0x103023a
     field public static final int Widget_Quantum_Button_Inset = 16974395; // 0x103023b
+    field public static final int Widget_Quantum_Button_Paper = 16974503; // 0x10302a7
+    field public static final int Widget_Quantum_Button_Paper_Color = 16974504; // 0x10302a8
     field public static final int Widget_Quantum_Button_Small = 16974396; // 0x103023c
     field public static final int Widget_Quantum_Button_Toggle = 16974397; // 0x103023d
     field public static final int Widget_Quantum_CalendarView = 16974398; // 0x103023e
@@ -2233,6 +2237,8 @@
     field public static final int Widget_Quantum_Light_Button = 16974452; // 0x1030274
     field public static final int Widget_Quantum_Light_Button_Borderless_Small = 16974453; // 0x1030275
     field public static final int Widget_Quantum_Light_Button_Inset = 16974454; // 0x1030276
+    field public static final int Widget_Quantum_Light_Button_Paper = 16974505; // 0x10302a9
+    field public static final int Widget_Quantum_Light_Button_Paper_Color = 16974506; // 0x10302aa
     field public static final int Widget_Quantum_Light_Button_Small = 16974455; // 0x1030277
     field public static final int Widget_Quantum_Light_Button_Toggle = 16974456; // 0x1030278
     field public static final int Widget_Quantum_Light_CalendarView = 16974457; // 0x1030279
@@ -4728,6 +4734,7 @@
     method public void onPasswordExpiring(android.content.Context, android.content.Intent);
     method public void onPasswordFailed(android.content.Context, android.content.Intent);
     method public void onPasswordSucceeded(android.content.Context, android.content.Intent);
+    method public void onProfileProvisioningComplete(android.content.Context, android.content.Intent);
     method public void onReceive(android.content.Context, android.content.Intent);
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
@@ -4736,6 +4743,7 @@
     field public static final java.lang.String ACTION_PASSWORD_EXPIRING = "android.app.action.ACTION_PASSWORD_EXPIRING";
     field public static final java.lang.String ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED";
     field public static final java.lang.String ACTION_PASSWORD_SUCCEEDED = "android.app.action.ACTION_PASSWORD_SUCCEEDED";
+    field public static final java.lang.String ACTION_PROFILE_PROVISIONING_COMPLETE = "android.app.action.ACTION_PROFILE_PROVISIONING_COMPLETE";
     field public static final java.lang.String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
     field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING";
   }
@@ -4788,6 +4796,7 @@
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void wipeData(int);
     field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
+    field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.managedprovisioning.ACTION_PROVISION_MANAGED_PROFILE";
     field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
     field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
     field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
@@ -4796,6 +4805,8 @@
     field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
     field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
     field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
+    field public static final java.lang.String EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME = "defaultManagedProfileName";
+    field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "deviceAdminPackageName";
     field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff
     field public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; // 0x0
     field public static final int KEYGUARD_DISABLE_SECURE_CAMERA = 2; // 0x2
@@ -6761,6 +6772,7 @@
     field public static final java.lang.String CATEGORY_HOME = "android.intent.category.HOME";
     field public static final java.lang.String CATEGORY_INFO = "android.intent.category.INFO";
     field public static final java.lang.String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER";
+    field public static final java.lang.String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
     field public static final java.lang.String CATEGORY_LE_DESK_DOCK = "android.intent.category.LE_DESK_DOCK";
     field public static final java.lang.String CATEGORY_MONKEY = "android.intent.category.MONKEY";
     field public static final java.lang.String CATEGORY_OPENABLE = "android.intent.category.OPENABLE";
@@ -13421,6 +13433,7 @@
 
   public static final class MediaMuxer.OutputFormat {
     field public static final int MUXER_OUTPUT_MPEG_4 = 0; // 0x0
+    field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1
   }
 
   public class MediaPlayer {
@@ -19381,6 +19394,10 @@
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
+    field public static final int CRYPT_TYPE_DEFAULT = 1; // 0x1
+    field public static final int CRYPT_TYPE_PASSWORD = 0; // 0x0
+    field public static final int CRYPT_TYPE_PATTERN = 2; // 0x2
+    field public static final int CRYPT_TYPE_PIN = 3; // 0x3
   }
 
 }
@@ -28123,6 +28140,7 @@
     field public static final int KEYCODE_SHIFT_LEFT = 59; // 0x3b
     field public static final int KEYCODE_SHIFT_RIGHT = 60; // 0x3c
     field public static final int KEYCODE_SLASH = 76; // 0x4c
+    field public static final int KEYCODE_SLEEP = 223; // 0xdf
     field public static final int KEYCODE_SOFT_LEFT = 1; // 0x1
     field public static final int KEYCODE_SOFT_RIGHT = 2; // 0x2
     field public static final int KEYCODE_SPACE = 62; // 0x3e
@@ -28144,6 +28162,7 @@
     field public static final int KEYCODE_VOLUME_MUTE = 164; // 0xa4
     field public static final int KEYCODE_VOLUME_UP = 24; // 0x18
     field public static final int KEYCODE_W = 51; // 0x33
+    field public static final int KEYCODE_WAKEUP = 224; // 0xe0
     field public static final int KEYCODE_WINDOW = 171; // 0xab
     field public static final int KEYCODE_X = 52; // 0x34
     field public static final int KEYCODE_Y = 53; // 0x35
@@ -29530,7 +29549,6 @@
     method public int getLayoutMode();
     method public android.animation.LayoutTransition getLayoutTransition();
     method public int getPersistentDrawingCache();
-    method public boolean hasIsolatedZVolume();
     method public int indexOfChild(android.view.View);
     method public final void invalidateChild(android.view.View, android.graphics.Rect);
     method public android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
@@ -29576,7 +29594,6 @@
     method public void setClipChildren(boolean);
     method public void setClipToPadding(boolean);
     method public void setDescendantFocusability(int);
-    method public void setIsolatedZVolume(boolean);
     method public void setLayoutAnimation(android.view.animation.LayoutAnimationController);
     method public void setLayoutAnimationListener(android.view.animation.Animation.AnimationListener);
     method public void setLayoutMode(int);
@@ -38403,6 +38420,11 @@
     method public abstract java.net.SocketImpl createSocketImpl();
   }
 
+  public abstract interface SocketOption {
+    method public abstract java.lang.String name();
+    method public abstract java.lang.Class<T> type();
+  }
+
   public abstract interface SocketOptions {
     method public abstract java.lang.Object getOption(int) throws java.net.SocketException;
     method public abstract void setOption(int, java.lang.Object) throws java.net.SocketException;
@@ -38433,6 +38455,21 @@
     ctor public SocketTimeoutException(java.lang.String);
   }
 
+  public final class StandardSocketOptions {
+    ctor public StandardSocketOptions();
+    field public static final java.net.SocketOption IP_MULTICAST_IF;
+    field public static final java.net.SocketOption IP_MULTICAST_LOOP;
+    field public static final java.net.SocketOption IP_MULTICAST_TTL;
+    field public static final java.net.SocketOption IP_TOS;
+    field public static final java.net.SocketOption SO_BROADCAST;
+    field public static final java.net.SocketOption SO_KEEPALIVE;
+    field public static final java.net.SocketOption SO_LINGER;
+    field public static final java.net.SocketOption SO_RCVBUF;
+    field public static final java.net.SocketOption SO_REUSEADDR;
+    field public static final java.net.SocketOption SO_SNDBUF;
+    field public static final java.net.SocketOption TCP_NODELAY;
+  }
+
   public final class URI implements java.lang.Comparable java.io.Serializable {
     ctor public URI(java.lang.String) throws java.net.URISyntaxException;
     ctor public URI(java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
@@ -38528,6 +38565,7 @@
     method public java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException;
     method public java.lang.String getContentEncoding();
     method public int getContentLength();
+    method public long getContentLengthLong();
     method public java.lang.String getContentType();
     method public long getDate();
     method public static boolean getDefaultAllowUserInteraction();
@@ -38542,6 +38580,7 @@
     method public long getHeaderFieldDate(java.lang.String, long);
     method public int getHeaderFieldInt(java.lang.String, int);
     method public java.lang.String getHeaderFieldKey(int);
+    method public long getHeaderFieldLong(java.lang.String, long);
     method public java.util.Map<java.lang.String, java.util.List<java.lang.String>> getHeaderFields();
     method public long getIfModifiedSince();
     method public java.io.InputStream getInputStream() throws java.io.IOException;
@@ -38892,6 +38931,10 @@
 
 package java.nio.channels {
 
+  public class AlreadyBoundException extends java.lang.IllegalStateException {
+    ctor public AlreadyBoundException();
+  }
+
   public class AlreadyConnectedException extends java.lang.IllegalStateException {
     ctor public AlreadyConnectedException();
   }
@@ -38941,8 +38984,11 @@
 
   public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
     ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
+    method public java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
     method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
     method public abstract java.nio.channels.DatagramChannel disconnect() throws java.io.IOException;
+    method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
+    method public T getOption(java.net.SocketOption<T>) throws java.io.IOException;
     method public abstract boolean isConnected();
     method public static java.nio.channels.DatagramChannel open() throws java.io.IOException;
     method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
@@ -38950,14 +38996,16 @@
     method public final synchronized long read(java.nio.ByteBuffer[]) throws java.io.IOException;
     method public abstract java.net.SocketAddress receive(java.nio.ByteBuffer) throws java.io.IOException;
     method public abstract int send(java.nio.ByteBuffer, java.net.SocketAddress) throws java.io.IOException;
+    method public java.nio.channels.DatagramChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
     method public abstract java.net.DatagramSocket socket();
+    method public java.util.Set<java.net.SocketOption<?>> supportedOptions();
     method public final int validOps();
     method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
     method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
     method public final synchronized long write(java.nio.ByteBuffer[]) throws java.io.IOException;
   }
 
-  public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
+  public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel java.nio.channels.SeekableByteChannel {
     ctor protected FileChannel();
     method public abstract void force(boolean) throws java.io.IOException;
     method public final java.nio.channels.FileLock lock() throws java.io.IOException;
@@ -39021,6 +39069,14 @@
     method public abstract void close() throws java.io.IOException;
   }
 
+  public abstract interface NetworkChannel implements java.lang.AutoCloseable java.nio.channels.Channel java.io.Closeable {
+    method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
+    method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
+    method public abstract T getOption(java.net.SocketOption<T>) throws java.io.IOException;
+    method public abstract java.nio.channels.NetworkChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+    method public abstract java.util.Set<java.net.SocketOption<?>> supportedOptions();
+  }
+
   public class NoConnectionPendingException extends java.lang.IllegalStateException {
     ctor public NoConnectionPendingException();
   }
@@ -39071,6 +39127,15 @@
     method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
   }
 
+  public abstract interface SeekableByteChannel implements java.nio.channels.ByteChannel {
+    method public abstract long position() throws java.io.IOException;
+    method public abstract java.nio.channels.SeekableByteChannel position(long) throws java.io.IOException;
+    method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
+    method public abstract long size() throws java.io.IOException;
+    method public abstract java.nio.channels.SeekableByteChannel truncate(long) throws java.io.IOException;
+    method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
+  }
+
   public abstract class SelectableChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.Channel {
     ctor protected SelectableChannel();
     method public abstract java.lang.Object blockingLock();
@@ -39119,18 +39184,27 @@
     method public abstract java.nio.channels.Selector wakeup();
   }
 
-  public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel {
+  public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.NetworkChannel {
     ctor protected ServerSocketChannel(java.nio.channels.spi.SelectorProvider);
     method public abstract java.nio.channels.SocketChannel accept() throws java.io.IOException;
+    method public final java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
+    method public java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
+    method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
+    method public T getOption(java.net.SocketOption<T>) throws java.io.IOException;
     method public static java.nio.channels.ServerSocketChannel open() throws java.io.IOException;
+    method public java.nio.channels.ServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
     method public abstract java.net.ServerSocket socket();
+    method public java.util.Set<java.net.SocketOption<?>> supportedOptions();
     method public final int validOps();
   }
 
-  public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
+  public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
     ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
+    method public java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
     method public abstract boolean connect(java.net.SocketAddress) throws java.io.IOException;
     method public abstract boolean finishConnect() throws java.io.IOException;
+    method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
+    method public T getOption(java.net.SocketOption<T>) throws java.io.IOException;
     method public abstract boolean isConnected();
     method public abstract boolean isConnectionPending();
     method public static java.nio.channels.SocketChannel open() throws java.io.IOException;
@@ -39138,7 +39212,9 @@
     method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
     method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
     method public final synchronized long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+    method public java.nio.channels.SocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
     method public abstract java.net.Socket socket();
+    method public java.util.Set<java.net.SocketOption<?>> supportedOptions();
     method public final int validOps();
     method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
     method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
@@ -45188,6 +45264,7 @@
     method public final int getWaitQueueLength(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
     method public final java.util.Collection<java.lang.Thread> getWaitingThreads(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
     method public final boolean hasContended();
+    method public final boolean hasQueuedPredecessors();
     method public final boolean hasQueuedThreads();
     method public final boolean hasWaiters(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
     method protected boolean isHeldExclusively();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index af4a362..287c463 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -21,6 +21,7 @@
 import android.transition.Transition;
 import android.transition.TransitionManager;
 import android.util.ArrayMap;
+import android.util.Pair;
 import android.util.SuperNotCalledException;
 import com.android.internal.app.ActionBarImpl;
 import com.android.internal.policy.PolicyManager;
@@ -3456,7 +3457,8 @@
     public void startActivityForResult(Intent intent, int requestCode) {
         Bundle options = null;
         if (mWindow.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
-            options = ActivityOptions.makeSceneTransitionAnimation(null).toBundle();
+            final Pair<View, String>[] noSharedElements = null;
+            options = ActivityOptions.makeSceneTransitionAnimation(noSharedElements).toBundle();
         }
         startActivityForResult(intent, requestCode, options);
     }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c877cd3..7f7616f 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -933,6 +933,16 @@
         }
     }
 
+    /** @hide */
+    public boolean isInHomeStack(int taskId) {
+        try {
+            return ActivityManagerNative.getDefault().isInHomeStack(taskId);
+        } catch (RemoteException e) {
+            // System dead, we will be dead too soon!
+            return false;
+        }
+    }
+
     /**
      * Flag for {@link #moveTaskToFront(int, int)}: also move the "home"
      * activity along with the task, so it is positioned immediately behind
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f4358e9..c7c81dd 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -654,6 +654,15 @@
             return true;
         }
 
+        case IS_IN_HOME_STACK_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            int taskId = data.readInt();
+            boolean isInHomeStack = isInHomeStack(taskId);
+            reply.writeNoException();
+            reply.writeInt(isInHomeStack ? 1 : 0);
+            return true;
+        }
+
         case SET_FOCUSED_STACK_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             int stackId = data.readInt();
@@ -2833,6 +2842,19 @@
         return info;
     }
     @Override
+    public boolean isInHomeStack(int taskId) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(taskId);
+        mRemote.transact(IS_IN_HOME_STACK_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean isInHomeStack = reply.readInt() > 0;
+        data.recycle();
+        reply.recycle();
+        return isInHomeStack;
+    }
+    @Override
     public void setFocusedStack(int stackId) throws RemoteException
     {
         Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ce2d806..69ada6a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -161,7 +161,7 @@
     private static final int LOG_ON_PAUSE_CALLED = 30021;
     private static final int LOG_ON_RESUME_CALLED = 30022;
 
-    static ContextImpl mSystemContext = null;
+    private ContextImpl mSystemContext;
 
     static IPackageManager sPackageManager;
 
@@ -752,8 +752,42 @@
 
             setCoreSettings(coreSettings);
 
-            // Tell the VMRuntime about the application.
-            VMRuntime.registerAppInfo(appInfo.dataDir, appInfo.processName);
+            /*
+             * Two possible indications that this package could be
+             * sharing its runtime with other packages:
+             *
+             * 1.) the sharedUserId attribute is set in the manifest,
+             *     indicating a request to share a VM with other
+             *     packages with the same sharedUserId.
+             *
+             * 2.) the application element of the manifest has an
+             *     attribute specifying a non-default process name,
+             *     indicating the desire to run in another packages VM.
+             *
+             * If sharing is enabled we do not have a unique application
+             * in a process and therefore cannot rely on the package
+             * name inside the runtime.
+             */
+            IPackageManager pm = getPackageManager();
+            android.content.pm.PackageInfo pi = null;
+            try {
+                pi = pm.getPackageInfo(appInfo.packageName, 0, UserHandle.myUserId());
+            } catch (RemoteException e) {
+            }
+            if (pi != null) {
+                boolean sharedUserIdSet = (pi.sharedUserId != null);
+                boolean processNameNotDefault =
+                (pi.applicationInfo != null &&
+                 !appInfo.packageName.equals(pi.applicationInfo.processName));
+                boolean sharable = (sharedUserIdSet || processNameNotDefault);
+
+                // Tell the VMRuntime about the application, unless it is shared
+                // inside a process.
+                if (!sharable) {
+                    VMRuntime.registerAppInfo(appInfo.packageName, appInfo.dataDir,
+                                            appInfo.processName);
+                }
+            }
 
             AppBindData data = new AppBindData();
             data.processName = processName;
@@ -1675,7 +1709,7 @@
                                 ? mBoundApplication.processName : null)
                         + ")");
                 packageInfo =
-                    new LoadedApk(this, aInfo, compatInfo, this, baseLoader,
+                    new LoadedApk(this, aInfo, compatInfo, baseLoader,
                             securityViolation, includeCode &&
                             (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);
                 if (includeCode) {
@@ -1728,26 +1762,15 @@
     public ContextImpl getSystemContext() {
         synchronized (this) {
             if (mSystemContext == null) {
-                ContextImpl context =
-                    ContextImpl.createSystemContext(this);
-                LoadedApk info = new LoadedApk(this, "android", context, null,
-                        CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
-                context.init(info, null, this);
-                context.getResources().updateConfiguration(mResourcesManager.getConfiguration(),
-                        mResourcesManager.getDisplayMetricsLocked(Display.DEFAULT_DISPLAY));
-                mSystemContext = context;
-                //Slog.i(TAG, "Created system resources " + context.getResources()
-                //        + ": " + context.getResources().getConfiguration());
+                mSystemContext = ContextImpl.createSystemContext(this);
             }
+            return mSystemContext;
         }
-        return mSystemContext;
     }
 
     public void installSystemApplicationInfo(ApplicationInfo info) {
         synchronized (this) {
-            ContextImpl context = getSystemContext();
-            context.init(new LoadedApk(this, "android", context, info,
-                    CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO), null, this);
+            getSystemContext().installSystemApplicationInfo(info);
 
             // give ourselves a default profiler
             mProfiler = new Profiler();
@@ -2234,8 +2257,7 @@
 
     private Context createBaseContextForActivity(ActivityClientRecord r,
             final Activity activity) {
-        ContextImpl appContext = new ContextImpl();
-        appContext.init(r.packageInfo, r.token, this);
+        ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
         appContext.setOuterContext(activity);
         Context baseContext = appContext;
 
@@ -2533,8 +2555,7 @@
                 agent = (BackupAgent) cl.loadClass(classname).newInstance();
 
                 // set up the agent's context
-                ContextImpl context = new ContextImpl();
-                context.init(packageInfo, null, this);
+                ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
                 context.setOuterContext(agent);
                 agent.attach(context);
 
@@ -2606,11 +2627,10 @@
         try {
             if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
 
-            ContextImpl context = new ContextImpl();
-            context.init(packageInfo, null, this);
+            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
+            context.setOuterContext(service);
 
             Application app = packageInfo.makeApplication(false, mInstrumentation);
-            context.setOuterContext(service);
             service.attach(context, this, data.info.name, data.token, app,
                     ActivityManagerNative.getDefault());
             service.onCreate();
@@ -2983,11 +3003,17 @@
                 int h;
                 if (w < 0) {
                     Resources res = r.activity.getResources();
-                    mThumbnailHeight = h =
-                        res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
-
-                    mThumbnailWidth = w =
-                        res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+                    if (SystemProperties.getBoolean("persist.recents.use_alternate", false)) {
+                        int wId = com.android.internal.R.dimen.recents_thumbnail_width;
+                        int hId = com.android.internal.R.dimen.recents_thumbnail_height;
+                        mThumbnailWidth = w = res.getDimensionPixelSize(wId);
+                        mThumbnailHeight = h = res.getDimensionPixelSize(hId);
+                    } else {
+                        mThumbnailHeight = h =
+                            res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
+                        mThumbnailWidth = w =
+                            res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+                    }
                 } else {
                     h = mThumbnailHeight;
                 }
@@ -4209,8 +4235,7 @@
         }
         updateDefaultDensity();
 
-        final ContextImpl appContext = new ContextImpl();
-        appContext.init(data.info, null, this);
+        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
         if (!Process.isIsolated()) {
             final File cacheDir = appContext.getCacheDir();
 
@@ -4326,8 +4351,7 @@
             instrApp.nativeLibraryDir = ii.nativeLibraryDir;
             LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                     appContext.getClassLoader(), false, true);
-            ContextImpl instrContext = new ContextImpl();
-            instrContext.init(pi, null, this);
+            ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
 
             try {
                 java.lang.ClassLoader cl = instrContext.getClassLoader();
@@ -4942,8 +4966,8 @@
                                                     UserHandle.myUserId());
             try {
                 mInstrumentation = new Instrumentation();
-                ContextImpl context = new ContextImpl();
-                context.init(getSystemContext().mPackageInfo, null, this);
+                ContextImpl context = ContextImpl.createAppContext(
+                        this, getSystemContext().mPackageInfo);
                 Application app = Instrumentation.newApplication(Application.class, context);
                 mAllApplications.add(app);
                 mInitialApplication = app;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9b3643c..0351292 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -185,22 +185,31 @@
      */
     private static ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>> sSharedPrefs;
 
-    /*package*/ LoadedApk mPackageInfo;
-    private String mBasePackageName;
-    private String mOpPackageName;
-    private Resources mResources;
-    /*package*/ ActivityThread mMainThread;
+    final ActivityThread mMainThread;
+    final LoadedApk mPackageInfo;
+
+    private final IBinder mActivityToken;
+
+    private final UserHandle mUser;
+
+    private final ApplicationContentResolver mContentResolver;
+
+    private final String mBasePackageName;
+    private final String mOpPackageName;
+
+    private final ResourcesManager mResourcesManager;
+    private final Resources mResources;
+    private final Display mDisplay; // may be null if default display
+    private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
+    private final Configuration mOverrideConfiguration;
+
+    private final boolean mRestricted;
+
     private Context mOuterContext;
-    private IBinder mActivityToken = null;
-    private ApplicationContentResolver mContentResolver;
     private int mThemeResource = 0;
     private Resources.Theme mTheme = null;
     private PackageManager mPackageManager;
-    private Display mDisplay; // may be null if default display
     private Context mReceiverRestrictedContext = null;
-    private boolean mRestricted;
-    private UserHandle mUser;
-    private ResourcesManager mResourcesManager;
 
     private final Object mSync = new Object();
 
@@ -222,8 +231,6 @@
 
     private static final String[] EMPTY_FILE_LIST = {};
 
-    final private DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
-
     /**
      * Override this class when the system service constructor needs a
      * ContextImpl.  Else, use StaticServiceFetcher below.
@@ -1887,20 +1894,17 @@
     @Override
     public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
             throws NameNotFoundException {
+        final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
         if (packageName.equals("system") || packageName.equals("android")) {
-            final ContextImpl context = new ContextImpl(mMainThread.getSystemContext());
-            context.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
-            context.init(mPackageInfo, null, mMainThread, mResources, mBasePackageName, user);
-            return context;
+            return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
+                    user, restricted, mDisplay, mOverrideConfiguration);
         }
 
-        LoadedApk pi =
-            mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags,
-                    user.getIdentifier());
+        LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
+                flags, user.getIdentifier());
         if (pi != null) {
-            ContextImpl c = new ContextImpl();
-            c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
-            c.init(pi, null, mMainThread, mResources, mBasePackageName, user);
+            ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
+                    user, restricted, mDisplay, mOverrideConfiguration);
             if (c.mResources != null) {
                 return c;
             }
@@ -1908,7 +1912,7 @@
 
         // Should be a better exception.
         throw new PackageManager.NameNotFoundException(
-            "Application package " + packageName + " not found");
+                "Application package " + packageName + " not found");
     }
 
     @Override
@@ -1917,12 +1921,8 @@
             throw new IllegalArgumentException("overrideConfiguration must not be null");
         }
 
-        ContextImpl c = new ContextImpl();
-        c.init(mPackageInfo, null, mMainThread);
-        c.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
-                mPackageInfo.getOverlayDirs(), getDisplayId(), overrideConfiguration,
-                mResources.getCompatibilityInfo(), mActivityToken);
-        return c;
+        return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
+                mUser, mRestricted, mDisplay, overrideConfiguration);
     }
 
     @Override
@@ -1931,15 +1931,8 @@
             throw new IllegalArgumentException("display must not be null");
         }
 
-        int displayId = display.getDisplayId();
-
-        ContextImpl context = new ContextImpl();
-        context.init(mPackageInfo, null, mMainThread);
-        context.mDisplay = display;
-        DisplayAdjustments daj = getDisplayAdjustments(displayId);
-        context.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
-                mPackageInfo.getOverlayDirs(), displayId, null, daj.getCompatibilityInfo(), null);
-        return context;
+        return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
+                mUser, mRestricted, display, mOverrideConfiguration);
     }
 
     private int getDisplayId() {
@@ -1981,43 +1974,76 @@
     }
 
     static ContextImpl createSystemContext(ActivityThread mainThread) {
-        final ContextImpl context = new ContextImpl();
-        context.init(Resources.getSystem(), mainThread, Process.myUserHandle());
+        LoadedApk packageInfo = new LoadedApk(mainThread);
+        ContextImpl context = new ContextImpl(null, mainThread,
+                packageInfo, null, null, false, null, null);
+        context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
+                context.mResourcesManager.getDisplayMetricsLocked(Display.DEFAULT_DISPLAY));
         return context;
     }
 
-    ContextImpl() {
+    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
+        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
+        return new ContextImpl(null, mainThread,
+                packageInfo, null, null, false, null, null);
+    }
+
+    static ContextImpl createActivityContext(ActivityThread mainThread,
+            LoadedApk packageInfo, IBinder activityToken) {
+        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
+        if (activityToken == null) throw new IllegalArgumentException("activityInfo");
+        return new ContextImpl(null, mainThread,
+                packageInfo, activityToken, null, false, null, null);
+    }
+
+    private ContextImpl(ContextImpl container, ActivityThread mainThread,
+            LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
+            Display display, Configuration overrideConfiguration) {
         mOuterContext = this;
-    }
 
-    /**
-     * Create a new ApplicationContext from an existing one.  The new one
-     * works and operates the same as the one it is copying.
-     *
-     * @param context Existing application context.
-     */
-    public ContextImpl(ContextImpl context) {
-        mPackageInfo = context.mPackageInfo;
-        mBasePackageName = context.mBasePackageName;
-        mOpPackageName = context.mOpPackageName;
-        mResources = context.mResources;
-        mMainThread = context.mMainThread;
-        mContentResolver = context.mContentResolver;
-        mUser = context.mUser;
-        mDisplay = context.mDisplay;
-        mOuterContext = this;
-        mDisplayAdjustments.setCompatibilityInfo(mPackageInfo.getCompatibilityInfo());
-    }
+        mMainThread = mainThread;
+        mActivityToken = activityToken;
+        mRestricted = restricted;
 
-    final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread) {
-        init(packageInfo, activityToken, mainThread, null, null, Process.myUserHandle());
-    }
+        if (user == null) {
+            user = Process.myUserHandle();
+        }
+        mUser = user;
 
-    final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread,
-            Resources container, String basePackageName, UserHandle user) {
         mPackageInfo = packageInfo;
-        if (basePackageName != null) {
-            mBasePackageName = mOpPackageName = basePackageName;
+        mContentResolver = new ApplicationContentResolver(this, mainThread, user);
+        mResourcesManager = ResourcesManager.getInstance();
+        mDisplay = display;
+        mOverrideConfiguration = overrideConfiguration;
+
+        final int displayId = getDisplayId();
+        CompatibilityInfo compatInfo = null;
+        if (container != null) {
+            compatInfo = container.getDisplayAdjustments(displayId).getCompatibilityInfo();
+        }
+        if (compatInfo == null && displayId == Display.DEFAULT_DISPLAY) {
+            compatInfo = packageInfo.getCompatibilityInfo();
+        }
+        mDisplayAdjustments.setCompatibilityInfo(compatInfo);
+        mDisplayAdjustments.setActivityToken(activityToken);
+
+        Resources resources = packageInfo.getResources(mainThread);
+        if (resources != null) {
+            if (activityToken != null
+                    || displayId != Display.DEFAULT_DISPLAY
+                    || overrideConfiguration != null
+                    || (compatInfo != null && compatInfo.applicationScale
+                            != resources.getCompatibilityInfo().applicationScale)) {
+                resources = mResourcesManager.getTopLevelResources(
+                        packageInfo.getResDir(), packageInfo.getOverlayDirs(), displayId,
+                        overrideConfiguration, compatInfo, activityToken);
+            }
+        }
+        mResources = resources;
+
+        if (container != null) {
+            mBasePackageName = container.mBasePackageName;
+            mOpPackageName = container.mOpPackageName;
         } else {
             mBasePackageName = packageInfo.mPackageName;
             ApplicationInfo ainfo = packageInfo.getApplicationInfo();
@@ -2031,45 +2057,10 @@
                 mOpPackageName = mBasePackageName;
             }
         }
-        mResources = mPackageInfo.getResources(mainThread);
-        mResourcesManager = ResourcesManager.getInstance();
-
-        CompatibilityInfo compatInfo =
-                container == null ? null : container.getCompatibilityInfo();
-        if (mResources != null &&
-                ((compatInfo != null && compatInfo.applicationScale !=
-                        mResources.getCompatibilityInfo().applicationScale)
-                || activityToken != null)) {
-            if (DEBUG) {
-                Log.d(TAG, "loaded context has different scaling. Using container's" +
-                        " compatiblity info:" + container.getDisplayMetrics());
-            }
-            if (compatInfo == null) {
-                compatInfo = packageInfo.getCompatibilityInfo();
-            }
-            mDisplayAdjustments.setCompatibilityInfo(compatInfo);
-            mDisplayAdjustments.setActivityToken(activityToken);
-            mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
-                    mPackageInfo.getOverlayDirs(), Display.DEFAULT_DISPLAY, null, compatInfo,
-                    activityToken);
-        } else {
-            mDisplayAdjustments.setCompatibilityInfo(packageInfo.getCompatibilityInfo());
-            mDisplayAdjustments.setActivityToken(activityToken);
-        }
-        mMainThread = mainThread;
-        mActivityToken = activityToken;
-        mContentResolver = new ApplicationContentResolver(this, mainThread, user);
-        mUser = user;
     }
 
-    final void init(Resources resources, ActivityThread mainThread, UserHandle user) {
-        mPackageInfo = null;
-        mBasePackageName = null;
-        mOpPackageName = null;
-        mResources = resources;
-        mMainThread = mainThread;
-        mContentResolver = new ApplicationContentResolver(this, mainThread, user);
-        mUser = user;
+    void installSystemApplicationInfo(ApplicationInfo info) {
+        mPackageInfo.installSystemApplicationInfo(info);
     }
 
     final void scheduleFinalCleanup(String who, String what) {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 1943bba..f2cabf4 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -122,6 +122,7 @@
     public void resizeStack(int stackId, Rect bounds) throws RemoteException;
     public List<StackInfo> getAllStackInfos() throws RemoteException;
     public StackInfo getStackInfo(int stackId) throws RemoteException;
+    public boolean isInHomeStack(int taskId) throws RemoteException;
     public void setFocusedStack(int stackId) throws RemoteException;
     public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
     /* oneway */
@@ -717,4 +718,5 @@
     // Start of L transactions
     int GET_TAG_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+210;
     int START_USER_IN_BACKGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+211;
+    int IS_IN_HOME_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+212;
 }
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 0115d1b..d409352 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -72,7 +72,7 @@
     private static final String TAG = "LoadedApk";
 
     private final ActivityThread mActivityThread;
-    private final ApplicationInfo mApplicationInfo;
+    private ApplicationInfo mApplicationInfo;
     final String mPackageName;
     private final String mAppDir;
     private final String mResDir;
@@ -111,8 +111,7 @@
      * so MUST NOT call back out to the activity manager.
      */
     public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
-            CompatibilityInfo compatInfo,
-            ActivityThread mainThread, ClassLoader baseLoader,
+            CompatibilityInfo compatInfo, ClassLoader baseLoader,
             boolean securityViolation, boolean includeCode) {
         mActivityThread = activityThread;
         mApplicationInfo = aInfo;
@@ -134,31 +133,17 @@
         mSecurityViolation = securityViolation;
         mIncludeCode = includeCode;
         mDisplayAdjustments.setCompatibilityInfo(compatInfo);
-
-        if (mAppDir == null) {
-            if (ActivityThread.mSystemContext == null) {
-                ActivityThread.mSystemContext =
-                    ContextImpl.createSystemContext(mainThread);
-                ResourcesManager resourcesManager = ResourcesManager.getInstance();
-                ActivityThread.mSystemContext.getResources().updateConfiguration(
-                        resourcesManager.getConfiguration(),
-                        resourcesManager.getDisplayMetricsLocked(
-                                 Display.DEFAULT_DISPLAY, mDisplayAdjustments), compatInfo);
-                //Slog.i(TAG, "Created system resources "
-                //        + mSystemContext.getResources() + ": "
-                //        + mSystemContext.getResources().getConfiguration());
-            }
-            mClassLoader = ActivityThread.mSystemContext.getClassLoader();
-            mResources = ActivityThread.mSystemContext.getResources();
-        }
     }
 
-    public LoadedApk(ActivityThread activityThread, String name,
-            Context systemContext, ApplicationInfo info, CompatibilityInfo compatInfo) {
+    /**
+     * Create information about the system package.
+     * Must call {@link #installSystemApplicationInfo} later.
+     */
+    LoadedApk(ActivityThread activityThread) {
         mActivityThread = activityThread;
-        mApplicationInfo = info != null ? info : new ApplicationInfo();
-        mApplicationInfo.packageName = name;
-        mPackageName = name;
+        mApplicationInfo = new ApplicationInfo();
+        mApplicationInfo.packageName = "android";
+        mPackageName = "android";
         mAppDir = null;
         mResDir = null;
         mOverlayDirs = null;
@@ -169,9 +154,16 @@
         mBaseClassLoader = null;
         mSecurityViolation = false;
         mIncludeCode = true;
-        mClassLoader = systemContext.getClassLoader();
-        mResources = systemContext.getResources();
-        mDisplayAdjustments.setCompatibilityInfo(compatInfo);
+        mClassLoader = ClassLoader.getSystemClassLoader();
+        mResources = Resources.getSystem();
+    }
+
+    /**
+     * Sets application info about the system package.
+     */
+    void installSystemApplicationInfo(ApplicationInfo info) {
+        assert info.packageName.equals("android");
+        mApplicationInfo = info;
     }
 
     public String getPackageName() {
@@ -513,8 +505,7 @@
 
         try {
             java.lang.ClassLoader cl = getClassLoader();
-            ContextImpl appContext = new ContextImpl();
-            appContext.init(this, null, mActivityThread);
+            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
             app = mActivityThread.mInstrumentation.newApplication(
                     cl, appClass, appContext);
             appContext.setOuterContext(app);
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 30b65de..f9d9059 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -29,25 +29,25 @@
  * Base class for implementing a device administration component.  This
  * class provides a convenience for interpreting the raw intent actions
  * that are sent by the system.
- * 
+ *
  * <p>The callback methods, like the base
  * {@link BroadcastReceiver#onReceive(Context, Intent) BroadcastReceiver.onReceive()}
  * method, happen on the main thread of the process.  Thus long running
  * operations must be done on another thread.  Note that because a receiver
  * is done once returning from its receive function, such long-running operations
  * should probably be done in a {@link Service}.
- * 
+ *
  * <p>When publishing your DeviceAdmin subclass as a receiver, it must
  * handle {@link #ACTION_DEVICE_ADMIN_ENABLED} and require the
  * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission.  A typical
  * manifest entry would look like:</p>
- * 
+ *
  * {@sample development/samples/ApiDemos/AndroidManifest.xml device_admin_declaration}
- *   
+ *
  * <p>The meta-data referenced here provides addition information specific
  * to the device administrator, as parsed by the {@link DeviceAdminInfo} class.
  * A typical file would be:</p>
- * 
+ *
  * {@sample development/samples/ApiDemos/res/xml/device_admin_sample.xml meta_data}
  *
  * <div class="special reference">
@@ -86,7 +86,7 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED
             = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
-    
+
     /**
      * A CharSequence that can be shown to the user informing them of the
      * impact of disabling your admin.
@@ -94,7 +94,7 @@
      * @see #ACTION_DEVICE_ADMIN_DISABLE_REQUESTED
      */
     public static final String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING";
-    
+
     /**
      * Action sent to a device administrator when the user has disabled
      * it.  Upon return, the application no longer has access to the
@@ -107,7 +107,7 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_DEVICE_ADMIN_DISABLED
             = "android.app.action.DEVICE_ADMIN_DISABLED";
-    
+
     /**
      * Action sent to a device administrator when the user has changed the
      * password of their device.  You can at this point check the characteristics
@@ -115,7 +115,7 @@
      * DevicePolicyManager.isActivePasswordSufficient()}.
      * You will generally
      * handle this in {@link DeviceAdminReceiver#onPasswordChanged}.
-     * 
+     *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to receive
      * this broadcast.
@@ -123,7 +123,7 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PASSWORD_CHANGED
             = "android.app.action.ACTION_PASSWORD_CHANGED";
-    
+
     /**
      * Action sent to a device administrator when the user has failed at
      * attempted to enter the password.  You can at this point check the
@@ -131,7 +131,7 @@
      * {@link DevicePolicyManager#getCurrentFailedPasswordAttempts
      * DevicePolicyManager.getCurrentFailedPasswordAttempts()}.  You will generally
      * handle this in {@link DeviceAdminReceiver#onPasswordFailed}.
-     * 
+     *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to receive
      * this broadcast.
@@ -139,11 +139,11 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PASSWORD_FAILED
             = "android.app.action.ACTION_PASSWORD_FAILED";
-    
+
     /**
      * Action sent to a device administrator when the user has successfully
      * entered their password, after failing one or more times.
-     * 
+     *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to receive
      * this broadcast.
@@ -165,15 +165,31 @@
             = "android.app.action.ACTION_PASSWORD_EXPIRING";
 
     /**
+     * Broadcast Action: This broadcast is sent to the newly created profile when
+     * the provisioning of a managed profile has completed successfully.
+     *
+     * <p>The broadcast is limited to the package which started the provisioning as specified in
+     * the extra {@link DevicePolicyManager#EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} of the
+     * {@link DevicePolicyManager#ACTION_PROVISION_MANAGED_PROFILE} intent that started the
+     * provisioning. It is also limited to the managed profile.
+     *
+     * <p>Input: Nothing.</p>
+     * <p>Output: Nothing</p>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PROFILE_PROVISIONING_COMPLETE =
+            "android.app.action.ACTION_PROFILE_PROVISIONING_COMPLETE";
+
+    /**
      * Name under which a DevicePolicy component publishes information
      * about itself.  This meta-data must reference an XML resource containing
      * a device-admin tag.  XXX TO DO: describe syntax.
      */
     public static final String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
-    
+
     private DevicePolicyManager mManager;
     private ComponentName mWho;
-    
+
     /**
      * Retrieve the DevicePolicyManager interface for this administrator to work
      * with the system.
@@ -186,7 +202,7 @@
                 Context.DEVICE_POLICY_SERVICE);
         return mManager;
     }
-    
+
     /**
      * Retrieve the ComponentName describing who this device administrator is, for
      * use in {@link DevicePolicyManager} APIs that require the administrator to
@@ -199,7 +215,7 @@
         mWho = new ComponentName(context, getClass());
         return mWho;
     }
-    
+
     /**
      * Called after the administrator is first enabled, as a result of
      * receiving {@link #ACTION_DEVICE_ADMIN_ENABLED}.  At this point you
@@ -209,7 +225,7 @@
      */
     public void onEnabled(Context context, Intent intent) {
     }
-    
+
     /**
      * Called when the user has asked to disable the administrator, as a result of
      * receiving {@link #ACTION_DEVICE_ADMIN_DISABLE_REQUESTED}, giving you
@@ -224,7 +240,7 @@
     public CharSequence onDisableRequested(Context context, Intent intent) {
         return null;
     }
-    
+
     /**
      * Called prior to the administrator being disabled, as a result of
      * receiving {@link #ACTION_DEVICE_ADMIN_DISABLED}.  Upon return, you
@@ -235,7 +251,7 @@
      */
     public void onDisabled(Context context, Intent intent) {
     }
-    
+
     /**
      * Called after the user has changed their password, as a result of
      * receiving {@link #ACTION_PASSWORD_CHANGED}.  At this point you
@@ -247,7 +263,7 @@
      */
     public void onPasswordChanged(Context context, Intent intent) {
     }
-    
+
     /**
      * Called after the user has failed at entering their current password, as a result of
      * receiving {@link #ACTION_PASSWORD_FAILED}.  At this point you
@@ -258,7 +274,7 @@
      */
     public void onPasswordFailed(Context context, Intent intent) {
     }
-    
+
     /**
      * Called after the user has succeeded at entering their current password,
      * as a result of receiving {@link #ACTION_PASSWORD_SUCCEEDED}.  This will
@@ -292,6 +308,26 @@
     }
 
     /**
+     * Called on the new profile when managed profile provisioning has completed.
+     * Managed profile provisioning is the process of setting up the device so that it has a
+     * separate profile which is managed by the mobile device management(mdm) application that
+     * triggered the provisioning.
+     *
+     * <p>As part of provisioning a new profile is created, the mdm is moved to the new profile and
+     * set as the owner of the profile so that it has full control over it.
+     * This intent is only received by the mdm package that is set as profile owner during
+     * provisioning.
+     *
+     * <p>Provisioning can be triggered via an intent with the action
+     * android.managedprovisioning.ACTION_PROVISION_MANAGED_PROFILE.
+     *
+     * @param context The running context as per {@link #onReceive}.
+     * @param intent The received intent as per {@link #onReceive}.
+     */
+    public void onProfileProvisioningComplete(Context context, Intent intent) {
+    }
+
+    /**
      * Intercept standard device administrator broadcasts.  Implementations
      * should not override this method; it is better to implement the
      * convenience callbacks for each action.
@@ -299,6 +335,7 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         String action = intent.getAction();
+
         if (ACTION_PASSWORD_CHANGED.equals(action)) {
             onPasswordChanged(context, intent);
         } else if (ACTION_PASSWORD_FAILED.equals(action)) {
@@ -317,6 +354,8 @@
             onDisabled(context, intent);
         } else if (ACTION_PASSWORD_EXPIRING.equals(action)) {
             onPasswordExpiring(context, intent);
+        } else if (ACTION_PROFILE_PROVISIONING_COMPLETE.equals(action)) {
+            onProfileProvisioningComplete(context, intent);
         }
     }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 1f41c56..e06cf38 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -77,6 +77,43 @@
     }
 
     /**
+     * Activity action: Starts the provisioning flow which sets up a managed profile.
+     * This intent will typically be sent by a mobile device management application(mdm).
+     * Managed profile provisioning creates a profile, moves the mdm to the profile,
+     * sets the mdm as the profile owner and removes all non required applications from the profile.
+     * As a profile owner the mdm than has full control over the managed profile.
+     *
+     * <p>The intent must contain the extras {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} and
+     * {@link #EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME}.
+     *
+     * <p> When managed provisioning has completed, an intent of the type
+     * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcasted to the
+     * mdm app on the managed profile.
+     *
+     * <p>Input: Nothing.</p>
+     * <p>Output: Nothing</p>
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_PROVISION_MANAGED_PROFILE
+        = "android.managedprovisioning.ACTION_PROVISION_MANAGED_PROFILE";
+
+    /**
+     * A String extra holding the name of the package of the mobile device management application
+     * that starts the managed provisioning flow. This package will be set as the profile owner.
+     * <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}.
+     */
+    public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME
+        = "deviceAdminPackageName";
+
+    /**
+     * A String extra holding the default name of the profile that is created during managed profile
+     * provisioning.
+     * <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}
+     */
+    public static final String EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME
+        = "defaultManagedProfileName";
+
+    /**
      * Activity action: ask the user to add a new device administrator to the system.
      * The desired policy is the ComponentName of the policy in the
      * {@link #EXTRA_DEVICE_ADMIN} extra field.  This will invoke a UI to
diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java
index b8b8f5f..333f825 100644
--- a/core/java/android/bluetooth/BluetoothInputDevice.java
+++ b/core/java/android/bluetooth/BluetoothInputDevice.java
@@ -79,6 +79,13 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_HANDSHAKE =
+        "android.bluetooth.input.profile.action.HANDSHAKE";
+
+    /**
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_REPORT =
         "android.bluetooth.input.profile.action.REPORT";
 
@@ -185,6 +192,11 @@
     /**
      * @hide
      */
+    public static final String EXTRA_STATUS = "android.bluetooth.BluetoothInputDevice.extra.STATUS";
+
+    /**
+     * @hide
+     */
     public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.BluetoothInputDevice.extra.VIRTUAL_UNPLUG_STATUS";
 
     private Context mContext;
@@ -608,7 +620,7 @@
      * @hide
      */
     public boolean setReport(BluetoothDevice device, byte reportType, String report) {
-        if (DBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
+        if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
         if (mService != null && isEnabled() && isValidDevice(device)) {
             try {
                 return mService.setReport(device, reportType, report);
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 9d0ab3a..02c850b 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -987,7 +987,7 @@
      * Implement this to handle requests to delete one or more rows.
      * The implementation should apply the selection clause when performing
      * deletion, allowing the operation to affect multiple rows in a directory.
-     * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyDelete()}
+     * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
      * after deleting.
      * This method can be called from multiple threads, as described in
      * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index f0b7ca8..96479e2 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2768,6 +2768,12 @@
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER";
     /**
+     * Indicates an activity optimized for Leanback mode, and that should
+     * be displayed in the Leanback launcher.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
+    /**
      * Provides information about the package it is in; typically used if
      * a package does not contain a {@link #CATEGORY_LAUNCHER} to provide
      * a front-door to the user without having to be shown in the all apps list.
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 8c23129..5f2b5f0 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1359,7 +1359,7 @@
         float q2 = rotationVector[1];
         float q3 = rotationVector[2];
 
-        if (rotationVector.length == 4) {
+        if (rotationVector.length >= 4) {
             q0 = rotationVector[3];
         } else {
             q0 = 1 - q1*q1 - q2*q2 - q3*q3;
@@ -1416,7 +1416,7 @@
      *  @param Q an array of floats in which to store the computed quaternion
      */
     public static void getQuaternionFromVector(float[] Q, float[] rv) {
-        if (rv.length == 4) {
+        if (rv.length >= 4) {
             Q[0] = rv[3];
         } else {
             Q[0] = 1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2];
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index f1e7e98..465d142 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -19,6 +19,7 @@
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.KeyboardLayout;
 import android.hardware.input.IInputDevicesChangedListener;
+import android.hardware.input.TouchCalibration;
 import android.os.IBinder;
 import android.view.InputDevice;
 import android.view.InputEvent;
@@ -39,6 +40,11 @@
     // applications, the caller must have the INJECT_EVENTS permission.
     boolean injectInputEvent(in InputEvent ev, int mode);
 
+    // Calibrate input device position
+    TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor, int rotation);
+    void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int rotation,
+            in TouchCalibration calibration);
+
     // Keyboard layouts configuration.
     KeyboardLayout[] getKeyboardLayouts();
     KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index a2aeafb..e3a3830 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -500,6 +500,45 @@
     }
 
     /**
+     * Gets the TouchCalibration applied to the specified input device's coordinates.
+     *
+     * @param inputDeviceDescriptor The input device descriptor.
+     * @return The TouchCalibration currently assigned for use with the given
+     * input device. If none is set, an identity TouchCalibration is returned.
+     *
+     * @hide
+     */
+    public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
+        try {
+            return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation);
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Could not get calibration matrix for input device.", ex);
+            return TouchCalibration.IDENTITY;
+        }
+    }
+
+    /**
+     * Sets the TouchCalibration to apply to the specified input device's coordinates.
+     * <p>
+     * This method may have the side-effect of causing the input device in question
+     * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}.
+     * </p>
+     *
+     * @param inputDeviceDescriptor The input device descriptor.
+     * @param calibration The calibration to be applied
+     *
+     * @hide
+     */
+    public void setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation,
+            TouchCalibration calibration) {
+        try {
+            mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation, calibration);
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Could not set calibration matrix for input device.", ex);
+        }
+    }
+
+    /**
      * Gets the mouse pointer speed.
      * <p>
      * Only returns the permanent mouse pointer speed.  Ignores any temporary pointer
diff --git a/core/java/android/hardware/input/TouchCalibration.aidl b/core/java/android/hardware/input/TouchCalibration.aidl
new file mode 100644
index 0000000..2c28774
--- /dev/null
+++ b/core/java/android/hardware/input/TouchCalibration.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.hardware.input;
+
+parcelable TouchCalibration;
diff --git a/core/java/android/hardware/input/TouchCalibration.java b/core/java/android/hardware/input/TouchCalibration.java
new file mode 100644
index 0000000..025fad0
--- /dev/null
+++ b/core/java/android/hardware/input/TouchCalibration.java
@@ -0,0 +1,126 @@
+/*
+ * 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 android.hardware.input;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Encapsulates calibration data for input devices.
+ *
+ * @hide
+ */
+public class TouchCalibration implements Parcelable {
+
+    public static final TouchCalibration IDENTITY = new TouchCalibration();
+
+    public static final Parcelable.Creator<TouchCalibration> CREATOR
+            = new Parcelable.Creator<TouchCalibration>() {
+        public TouchCalibration createFromParcel(Parcel in) {
+            return new TouchCalibration(in);
+        }
+
+        public TouchCalibration[] newArray(int size) {
+            return new TouchCalibration[size];
+        }
+    };
+
+    private final float mXScale, mXYMix, mXOffset;
+    private final float mYXMix, mYScale, mYOffset;
+
+    /**
+     * Create a new TouchCalibration initialized to the identity transformation.
+     */
+    public TouchCalibration() {
+        this(1,0,0,0,1,0);
+    }
+
+    /**
+     * Create a new TouchCalibration from affine transformation paramters.
+     * @param xScale   Influence of input x-axis value on output x-axis value.
+     * @param xyMix    Influence of input y-axis value on output x-axis value.
+     * @param xOffset  Constant offset to be applied to output x-axis value.
+     * @param yXMix    Influence of input x-axis value on output y-axis value.
+     * @param yScale   Influence of input y-axis value on output y-axis value.
+     * @param yOffset  Constant offset to be applied to output y-axis value.
+     */
+    public TouchCalibration(float xScale, float xyMix, float xOffset,
+            float yxMix, float yScale, float yOffset) {
+        mXScale  = xScale;
+        mXYMix   = xyMix;
+        mXOffset = xOffset;
+        mYXMix   = yxMix;
+        mYScale  = yScale;
+        mYOffset = yOffset;
+    }
+
+    public TouchCalibration(Parcel in) {
+        mXScale  = in.readFloat();
+        mXYMix   = in.readFloat();
+        mXOffset = in.readFloat();
+        mYXMix   = in.readFloat();
+        mYScale  = in.readFloat();
+        mYOffset = in.readFloat();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeFloat(mXScale);
+        dest.writeFloat(mXYMix);
+        dest.writeFloat(mXOffset);
+        dest.writeFloat(mYXMix);
+        dest.writeFloat(mYScale);
+        dest.writeFloat(mYOffset);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public float[] getAffineTransform() {
+        return new float[] { mXScale, mXYMix, mXOffset, mYXMix, mYScale, mYOffset };
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        } else if (obj instanceof TouchCalibration) {
+            TouchCalibration cal = (TouchCalibration)obj;
+
+            return (cal.mXScale  == mXScale)  &&
+                   (cal.mXYMix   == mXYMix)   &&
+                   (cal.mXOffset == mXOffset) &&
+                   (cal.mYXMix   == mYXMix)   &&
+                   (cal.mYScale  == mYScale)  &&
+                   (cal.mYOffset == mYOffset);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Float.floatToIntBits(mXScale)  ^
+               Float.floatToIntBits(mXYMix)   ^
+               Float.floatToIntBits(mXOffset) ^
+               Float.floatToIntBits(mYXMix)   ^
+               Float.floatToIntBits(mYScale)  ^
+               Float.floatToIntBits(mYOffset);
+    }
+}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index cc4bb51..7db4ac2 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -343,8 +343,9 @@
         }
 
         public class Pid {
-            public long mWakeSum;
-            public long mWakeStart;
+            public int mWakeNesting;
+            public long mWakeSumMs;
+            public long mWakeStartMs;
         }
 
         /**
@@ -515,7 +516,8 @@
         public static final byte CMD_UPDATE = 0;        // These can be written as deltas
         public static final byte CMD_NULL = -1;
         public static final byte CMD_START = 4;
-        public static final byte CMD_OVERFLOW = 5;
+        public static final byte CMD_CURRENT_TIME = 5;
+        public static final byte CMD_OVERFLOW = 6;
 
         public byte cmd = CMD_NULL;
         
@@ -578,6 +580,9 @@
         // The wake lock that was acquired at this point.
         public HistoryTag wakelockTag;
 
+        // Kernel wakeup reason at this point.
+        public HistoryTag wakeReasonTag;
+
         public static final int EVENT_FLAG_START = 0x8000;
         public static final int EVENT_FLAG_FINISH = 0x4000;
 
@@ -607,11 +612,15 @@
         public int eventCode;
         public HistoryTag eventTag;
 
+        // Only set for CMD_CURRENT_TIME.
+        public long currentTime;
+
         // Meta-data when reading.
         public int numReadInts;
 
         // Pre-allocated objects.
         public final HistoryTag localWakelockTag = new HistoryTag();
+        public final HistoryTag localWakeReasonTag = new HistoryTag();
         public final HistoryTag localEventTag = new HistoryTag();
 
         public HistoryItem() {
@@ -633,23 +642,28 @@
                     | ((((int)batteryLevel)<<8)&0xff00)
                     | ((((int)batteryStatus)<<16)&0xf0000)
                     | ((((int)batteryHealth)<<20)&0xf00000)
-                    | ((((int)batteryPlugType)<<24)&0xf000000);
+                    | ((((int)batteryPlugType)<<24)&0xf000000)
+                    | (wakelockTag != null ? 0x10000000 : 0)
+                    | (wakeReasonTag != null ? 0x20000000 : 0)
+                    | (eventCode != EVENT_NONE ? 0x40000000 : 0);
             dest.writeInt(bat);
             bat = (((int)batteryTemperature)&0xffff)
                     | ((((int)batteryVoltage)<<16)&0xffff0000);
             dest.writeInt(bat);
             dest.writeInt(states);
             if (wakelockTag != null) {
-                dest.writeInt(1);
                 wakelockTag.writeToParcel(dest, flags);
-            } else {
-                dest.writeInt(0);
             }
-            dest.writeInt(eventCode);
+            if (wakeReasonTag != null) {
+                wakeReasonTag.writeToParcel(dest, flags);
+            }
             if (eventCode != EVENT_NONE) {
                 dest.writeInt(eventCode);
                 eventTag.writeToParcel(dest, flags);
             }
+            if (cmd == CMD_CURRENT_TIME) {
+                dest.writeLong(currentTime);
+            }
         }
 
         public void readFromParcel(Parcel src) {
@@ -660,20 +674,34 @@
             batteryStatus = (byte)((bat>>16)&0xf);
             batteryHealth = (byte)((bat>>20)&0xf);
             batteryPlugType = (byte)((bat>>24)&0xf);
-            bat = src.readInt();
-            batteryTemperature = (short)(bat&0xffff);
-            batteryVoltage = (char)((bat>>16)&0xffff);
+            int bat2 = src.readInt();
+            batteryTemperature = (short)(bat2&0xffff);
+            batteryVoltage = (char)((bat2>>16)&0xffff);
             states = src.readInt();
-            if (src.readInt() != 0) {
+            if ((bat&0x10000000) != 0) {
                 wakelockTag = localWakelockTag;
                 wakelockTag.readFromParcel(src);
             } else {
                 wakelockTag = null;
             }
-            eventCode = src.readInt();
-            if (eventCode != EVENT_NONE) {
+            if ((bat&0x20000000) != 0) {
+                wakeReasonTag = localWakeReasonTag;
+                wakeReasonTag.readFromParcel(src);
+            } else {
+                wakeReasonTag = null;
+            }
+            if ((bat&0x40000000) != 0) {
+                eventCode = src.readInt();
                 eventTag = localEventTag;
                 eventTag.readFromParcel(src);
+            } else {
+                eventCode = EVENT_NONE;
+                eventTag = null;
+            }
+            if (cmd == CMD_CURRENT_TIME) {
+                currentTime = src.readLong();
+            } else {
+                currentTime = 0;
             }
             numReadInts += (src.dataPosition()-start)/4;
         }
@@ -689,6 +717,7 @@
             batteryVoltage = 0;
             states = 0;
             wakelockTag = null;
+            wakeReasonTag = null;
             eventCode = EVENT_NONE;
             eventTag = null;
         }
@@ -719,6 +748,12 @@
             } else {
                 wakelockTag = null;
             }
+            if (o.wakeReasonTag != null) {
+                wakeReasonTag = localWakeReasonTag;
+                wakeReasonTag.setTo(o.wakeReasonTag);
+            } else {
+                wakeReasonTag = null;
+            }
             eventCode = o.eventCode;
             if (o.eventTag != null) {
                 eventTag = localEventTag;
@@ -726,6 +761,7 @@
             } else {
                 eventTag = null;
             }
+            currentTime = o.currentTime;
         }
 
         public boolean sameNonEvent(HistoryItem o) {
@@ -735,7 +771,8 @@
                     && batteryPlugType == o.batteryPlugType
                     && batteryTemperature == o.batteryTemperature
                     && batteryVoltage == o.batteryVoltage
-                    && states == o.states;
+                    && states == o.states
+                    && currentTime == o.currentTime;
         }
 
         public boolean same(HistoryItem o) {
@@ -750,6 +787,14 @@
                     return false;
                 }
             }
+            if (wakeReasonTag != o.wakeReasonTag) {
+                if (wakeReasonTag == null || o.wakeReasonTag == null) {
+                    return false;
+                }
+                if (!wakeReasonTag.equals(o.wakeReasonTag)) {
+                    return false;
+                }
+            }
             if (eventTag != o.eventTag) {
                 if (eventTag == null || o.eventTag == null) {
                     return false;
@@ -1916,51 +1961,6 @@
         long fullWakeLockTimeTotalMicros = 0;
         long partialWakeLockTimeTotalMicros = 0;
 
-        final Comparator<TimerEntry> timerComparator = new Comparator<TimerEntry>() {
-            @Override
-            public int compare(TimerEntry lhs, TimerEntry rhs) {
-                long lhsTime = lhs.mTime;
-                long rhsTime = rhs.mTime;
-                if (lhsTime < rhsTime) {
-                    return 1;
-                }
-                if (lhsTime > rhsTime) {
-                    return -1;
-                }
-                return 0;
-            }
-        };
-
-        if (reqUid < 0) {
-            Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
-            if (kernelWakelocks.size() > 0) {
-                final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>();
-                for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
-                    BatteryStats.Timer timer = ent.getValue();
-                    long totalTimeMillis = computeWakeLock(timer, rawRealtime, which);
-                    if (totalTimeMillis > 0) {
-                        timers.add(new TimerEntry(ent.getKey(), 0, timer, totalTimeMillis));
-                    }
-                }
-                Collections.sort(timers, timerComparator);
-                for (int i=0; i<timers.size(); i++) {
-                    TimerEntry timer = timers.get(i);
-                    String linePrefix = ": ";
-                    sb.setLength(0);
-                    sb.append(prefix);
-                    sb.append("  Kernel Wake lock ");
-                    sb.append(timer.mName);
-                    linePrefix = printWakeLock(sb, timer.mTimer, rawRealtime, null,
-                            which, linePrefix);
-                    if (!linePrefix.equals(": ")) {
-                        sb.append(" realtime");
-                        // Only print out wake locks that were held
-                        pw.println(sb.toString());
-                    }
-                }
-            }
-        }
-
         final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>();
 
         for (int iu = 0; iu < NU; iu++) {
@@ -2292,6 +2292,55 @@
             pw.println();
         }
 
+        final Comparator<TimerEntry> timerComparator = new Comparator<TimerEntry>() {
+            @Override
+            public int compare(TimerEntry lhs, TimerEntry rhs) {
+                long lhsTime = lhs.mTime;
+                long rhsTime = rhs.mTime;
+                if (lhsTime < rhsTime) {
+                    return 1;
+                }
+                if (lhsTime > rhsTime) {
+                    return -1;
+                }
+                return 0;
+            }
+        };
+
+        if (reqUid < 0) {
+            Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
+            if (kernelWakelocks.size() > 0) {
+                final ArrayList<TimerEntry> ktimers = new ArrayList<TimerEntry>();
+                for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
+                    BatteryStats.Timer timer = ent.getValue();
+                    long totalTimeMillis = computeWakeLock(timer, rawRealtime, which);
+                    if (totalTimeMillis > 0) {
+                        ktimers.add(new TimerEntry(ent.getKey(), 0, timer, totalTimeMillis));
+                    }
+                }
+                if (ktimers.size() > 0) {
+                    Collections.sort(ktimers, timerComparator);
+                    pw.print(prefix); pw.println("  All kernel wake locks:");
+                    for (int i=0; i<ktimers.size(); i++) {
+                        TimerEntry timer = ktimers.get(i);
+                        String linePrefix = ": ";
+                        sb.setLength(0);
+                        sb.append(prefix);
+                        sb.append("  Kernel Wake lock ");
+                        sb.append(timer.mName);
+                        linePrefix = printWakeLock(sb, timer.mTimer, rawRealtime, null,
+                                which, linePrefix);
+                        if (!linePrefix.equals(": ")) {
+                            sb.append(" realtime");
+                            // Only print out wake locks that were held
+                            pw.println(sb.toString());
+                        }
+                    }
+                    pw.println();
+                }
+            }
+        }
+
         if (timers.size() > 0) {
             Collections.sort(timers, timerComparator);
             pw.print(prefix); pw.println("  All partial wake locks:");
@@ -2727,14 +2776,22 @@
         public void printNextItem(PrintWriter pw, HistoryItem rec, long now, boolean checkin) {
             if (!checkin) {
                 pw.print("  ");
-                TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+                if (now >= 0) {
+                    TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+                } else {
+                    TimeUtils.formatDuration(rec.time, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+                }
                 pw.print(" (");
                 pw.print(rec.numReadInts);
                 pw.print(") ");
             } else {
                 if (lastTime < 0) {
-                    pw.print("@");
-                    pw.print(rec.time-now);
+                    if (now >= 0) {
+                        pw.print("@");
+                        pw.print(rec.time-now);
+                    } else {
+                        pw.print(rec.time);
+                    }
                 } else {
                     pw.print(rec.time-lastTime);
                 }
@@ -2745,6 +2802,18 @@
                     pw.print(":");
                 }
                 pw.println("START");
+            } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
+                if (checkin) {
+                    pw.print(":");
+                }
+                pw.print("TIME:");
+                if (checkin) {
+                    pw.println(rec.currentTime);
+                } else {
+                    pw.print(" ");
+                    pw.println(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+                            rec.currentTime).toString());
+                }
             } else if (rec.cmd == HistoryItem.CMD_OVERFLOW) {
                 if (checkin) {
                     pw.print(":");
@@ -2858,6 +2927,18 @@
                 }
                 printBitDescriptions(pw, oldState, rec.states, rec.wakelockTag,
                         HISTORY_STATE_DESCRIPTIONS, !checkin);
+                if (rec.wakeReasonTag != null) {
+                    if (checkin) {
+                        pw.print(",Wr=");
+                        pw.print(rec.wakeReasonTag.poolIdx);
+                    } else {
+                        pw.print(" wake_reason=");
+                        pw.print(rec.wakeReasonTag.uid);
+                        pw.print(":\"");
+                        pw.print(rec.wakeReasonTag.string);
+                        pw.print("\"");
+                    }
+                }
                 if (rec.eventCode != HistoryItem.EVENT_NONE) {
                     pw.print(checkin ? "," : " ");
                     if ((rec.eventCode&HistoryItem.EVENT_FLAG_START) != 0) {
@@ -2886,8 +2967,8 @@
                     }
                 }
                 pw.println();
+                oldState = rec.states;
             }
-            oldState = rec.states;
         }
     }
 
@@ -2918,108 +2999,130 @@
         pw.print(suffix);
     }
 
+    public static final int DUMP_UNPLUGGED_ONLY = 1<<0;
+    public static final int DUMP_CHARGED_ONLY = 1<<1;
+    public static final int DUMP_HISTORY_ONLY = 1<<2;
+    public static final int DUMP_INCLUDE_HISTORY = 1<<3;
+
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
      *
      * @param pw a Printer to receive the dump output.
      */
     @SuppressWarnings("unused")
-    public void dumpLocked(Context context, PrintWriter pw, boolean isUnpluggedOnly, int reqUid,
-            boolean historyOnly) {
+    public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
         prepareForDumpLocked();
 
-        long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
+        final boolean filtering =
+                (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0;
 
-        final HistoryItem rec = new HistoryItem();
-        final long historyTotalSize = getHistoryTotalSize();
-        final long historyUsedSize = getHistoryUsedSize();
-        if (startIteratingHistoryLocked()) {
-            try {
-                pw.print("Battery History (");
-                pw.print((100*historyUsedSize)/historyTotalSize);
-                pw.print("% used, ");
-                printSizeValue(pw, historyUsedSize);
-                pw.print(" used of ");
-                printSizeValue(pw, historyTotalSize);
-                pw.print(", ");
-                pw.print(getHistoryStringPoolSize());
-                pw.print(" strings using ");
-                printSizeValue(pw, getHistoryStringPoolBytes());
-                pw.println("):");
-                HistoryPrinter hprinter = new HistoryPrinter();
-                while (getNextHistoryLocked(rec)) {
-                    hprinter.printNextItem(pw, rec, now, false);
+        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()) {
+                try {
+                    pw.print("Battery History (");
+                    pw.print((100*historyUsedSize)/historyTotalSize);
+                    pw.print("% used, ");
+                    printSizeValue(pw, historyUsedSize);
+                    pw.print(" used of ");
+                    printSizeValue(pw, historyTotalSize);
+                    pw.print(", ");
+                    pw.print(getHistoryStringPoolSize());
+                    pw.print(" strings using ");
+                    printSizeValue(pw, getHistoryStringPoolBytes());
+                    pw.println("):");
+                    HistoryPrinter hprinter = new HistoryPrinter();
+                    long lastTime = -1;
+                    while (getNextHistoryLocked(rec)) {
+                        lastTime = rec.time;
+                        if (rec.time >= histStart) {
+                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, false);
+                        }
+                    }
+                    if (histStart >= 0) {
+                        pw.print("  NEXT: "); pw.println(lastTime+1);
+                    }
+                    pw.println();
+                } finally {
+                    finishIteratingHistoryLocked();
                 }
-                pw.println();
-            } finally {
-                finishIteratingHistoryLocked();
+            }
+
+            if (startIteratingOldHistoryLocked()) {
+                try {
+                    pw.println("Old battery History:");
+                    HistoryPrinter hprinter = new HistoryPrinter();
+                    while (getNextOldHistoryLocked(rec)) {
+                        hprinter.printNextItem(pw, rec, now, false);
+                    }
+                    pw.println();
+                } finally {
+                    finishIteratingOldHistoryLocked();
+                }
             }
         }
 
-        if (startIteratingOldHistoryLocked()) {
-            try {
-                pw.println("Old battery History:");
-                HistoryPrinter hprinter = new HistoryPrinter();
-                while (getNextOldHistoryLocked(rec)) {
-                    hprinter.printNextItem(pw, rec, now, false);
-                }
-                pw.println();
-            } finally {
-                finishIteratingOldHistoryLocked();
-            }
-        }
-
-        if (historyOnly) {
+        if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) == 0) {
             return;
         }
 
-        SparseArray<? extends Uid> uidStats = getUidStats();
-        final int NU = uidStats.size();
-        boolean didPid = false;
-        long nowRealtime = SystemClock.elapsedRealtime();
-        for (int i=0; i<NU; i++) {
-            Uid uid = uidStats.valueAt(i);
-            SparseArray<? extends Uid.Pid> pids = uid.getPidStats();
-            if (pids != null) {
-                for (int j=0; j<pids.size(); j++) {
-                    Uid.Pid pid = pids.valueAt(j);
-                    if (!didPid) {
-                        pw.println("Per-PID Stats:");
-                        didPid = true;
+        if (!filtering) {
+            SparseArray<? extends Uid> uidStats = getUidStats();
+            final int NU = uidStats.size();
+            boolean didPid = false;
+            long nowRealtime = SystemClock.elapsedRealtime();
+            for (int i=0; i<NU; i++) {
+                Uid uid = uidStats.valueAt(i);
+                SparseArray<? extends Uid.Pid> pids = uid.getPidStats();
+                if (pids != null) {
+                    for (int j=0; j<pids.size(); j++) {
+                        Uid.Pid pid = pids.valueAt(j);
+                        if (!didPid) {
+                            pw.println("Per-PID Stats:");
+                            didPid = true;
+                        }
+                        long time = pid.mWakeSumMs + (pid.mWakeNesting > 0
+                                ? (nowRealtime - pid.mWakeStartMs) : 0);
+                        pw.print("  PID "); pw.print(pids.keyAt(j));
+                                pw.print(" wake time: ");
+                                TimeUtils.formatDuration(time, pw);
+                                pw.println("");
                     }
-                    long time = pid.mWakeSum + (pid.mWakeStart != 0
-                            ? (nowRealtime - pid.mWakeStart) : 0);
-                    pw.print("  PID "); pw.print(pids.keyAt(j));
-                            pw.print(" wake time: ");
-                            TimeUtils.formatDuration(time, pw);
-                            pw.println("");
                 }
             }
-        }
-        if (didPid) {
-            pw.println("");
+            if (didPid) {
+                pw.println("");
+            }
         }
 
-        if (!isUnpluggedOnly) {
+        if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
             pw.println("Statistics since last charge:");
             pw.println("  System starts: " + getStartCount()
                     + ", currently on battery: " + getIsOnBattery());
             dumpLocked(context, pw, "", STATS_SINCE_CHARGED, reqUid);
             pw.println("");
         }
-        pw.println("Statistics since last unplugged:");
-        dumpLocked(context, pw, "", STATS_SINCE_UNPLUGGED, reqUid);
+        if (!filtering || (flags&DUMP_UNPLUGGED_ONLY) != 0) {
+            pw.println("Statistics since last unplugged:");
+            dumpLocked(context, pw, "", STATS_SINCE_UNPLUGGED, reqUid);
+        }
     }
     
     @SuppressWarnings("unused")
-    public void dumpCheckinLocked(Context context,
-            PrintWriter pw, List<ApplicationInfo> apps, boolean isUnpluggedOnly,
-            boolean includeHistory, boolean historyOnly) {
+    public void dumpCheckinLocked(Context context, PrintWriter pw,
+            List<ApplicationInfo> apps, int flags, long histStart) {
         prepareForDumpLocked();
         
         long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
 
-        if (includeHistory || historyOnly) {
+        final boolean filtering =
+                (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 {
@@ -3034,10 +3137,17 @@
                         pw.println();
                     }
                     HistoryPrinter hprinter = new HistoryPrinter();
+                    long lastTime = -1;
                     while (getNextHistoryLocked(rec)) {
-                        pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
-                        pw.print(HISTORY_DATA); pw.print(',');
-                        hprinter.printNextItem(pw, rec, now, true);
+                        lastTime = rec.time;
+                        if (rec.time >= histStart) {
+                            pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                            pw.print(HISTORY_DATA); pw.print(',');
+                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, true);
+                        }
+                    }
+                    if (histStart >= 0) {
+                        pw.print("NEXT: "); pw.println(lastTime+1);
                     }
                 } finally {
                     finishIteratingHistoryLocked();
@@ -3045,7 +3155,7 @@
             }
         }
 
-        if (historyOnly) {
+        if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) == 0) {
             return;
         }
 
@@ -3076,11 +3186,10 @@
                 }
             }
         }
-        if (isUnpluggedOnly) {
-            dumpCheckinLocked(context, pw, STATS_SINCE_UNPLUGGED, -1);
-        }
-        else {
+        if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
             dumpCheckinLocked(context, pw, STATS_SINCE_CHARGED, -1);
+        }
+        if (!filtering || (flags&DUMP_UNPLUGGED_ONLY) != 0) {
             dumpCheckinLocked(context, pw, STATS_SINCE_UNPLUGGED, -1);
         }
     }
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 6650fca..5d55143 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -21,11 +21,11 @@
 /**
  * Class that operates the vibrator on the device.
  * <p>
- * If your process exits, any vibration you started with will stop.
+ * If your process exits, any vibration you started will stop.
  * </p>
  *
  * To obtain an instance of the system vibrator, call
- * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as argument.
+ * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as the argument.
  */
 public abstract class Vibrator {
     /**
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 68b91cb..4963991 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -58,6 +58,24 @@
  * argument of {@link android.content.Context#STORAGE_SERVICE}.
  */
 public class StorageManager {
+
+    /// Consts to match the password types in cryptfs.h
+    /** Master key is encrypted with a password.
+     */
+    public static final int CRYPT_TYPE_PASSWORD = 0;
+
+    /** Master key is encrypted with the default password.
+     */
+    public static final int CRYPT_TYPE_DEFAULT = 1;
+
+    /** Master key is encrypted with a pattern.
+     */
+    public static final int CRYPT_TYPE_PATTERN = 2;
+
+    /** Master key is encrypted with a pin.
+     */
+    public static final int CRYPT_TYPE_PIN = 3;
+
     private static final String TAG = "StorageManager";
 
     private final ContentResolver mResolver;
@@ -645,14 +663,4 @@
         return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
                 DEFAULT_FULL_THRESHOLD_BYTES);
     }
-
-    /// Consts to match the password types in cryptfs.h
-    /** @hide */
-    public static final int CRYPT_TYPE_PASSWORD = 0;
-    /** @hide */
-    public static final int CRYPT_TYPE_DEFAULT = 1;
-    /** @hide */
-    public static final int CRYPT_TYPE_PATTERN = 2;
-    /** @hide */
-    public static final int CRYPT_TYPE_PIN = 3;
 }
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index 9522112..0f8da44 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -191,8 +191,6 @@
         for (int i = 1; i <= numGroups; i++) {
             String s = matcher.group(i);
 
-            System.err.println("Group(" + i + ") : " + s);
-
             if (s != null) {
                 b.append(s);
             }
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
index 0ae36c1..be6f401 100644
--- a/core/java/android/view/DisplayList.java
+++ b/core/java/android/view/DisplayList.java
@@ -122,9 +122,6 @@
  * @hide
  */
 public class DisplayList {
-    private boolean mValid;
-    private final long mNativeDisplayList;
-
     /**
      * Flag used when calling
      * {@link HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int)}
@@ -175,6 +172,10 @@
      */
     public static final int STATUS_DREW = 0x4;
 
+    private boolean mValid;
+    private final long mNativeDisplayList;
+    private HardwareRenderer mRenderer;
+
     private DisplayList(String name) {
         mNativeDisplayList = nCreate();
         nSetDisplayListName(mNativeDisplayList, name);
@@ -233,7 +234,13 @@
         GLES20RecordingCanvas canvas = (GLES20RecordingCanvas) endCanvas;
         canvas.onPostDraw();
         long displayListData = canvas.finishRecording();
-        renderer.swapDisplayListData(mNativeDisplayList, displayListData);
+        if (renderer != mRenderer) {
+            // If we are changing renderers first destroy with the old
+            // renderer, then set with the new one
+            destroyDisplayListData();
+        }
+        mRenderer = renderer;
+        setDisplayListData(displayListData);
         canvas.recycle();
         mValid = true;
     }
@@ -245,14 +252,22 @@
      *
      * @hide
      */
-    public void destroyDisplayListData(HardwareRenderer renderer) {
-        if (renderer == null) {
-            throw new IllegalArgumentException("Cannot destroyDisplayListData with a null renderer");
-        }
-        renderer.swapDisplayListData(mNativeDisplayList, 0);
+    public void destroyDisplayListData() {
+        if (!mValid) return;
+
+        setDisplayListData(0);
+        mRenderer = null;
         mValid = false;
     }
 
+    private void setDisplayListData(long newData) {
+        if (mRenderer != null) {
+            mRenderer.setDisplayListData(mNativeDisplayList, newData);
+        } else {
+            throw new IllegalStateException("Trying to set data without a renderer! data=" + newData);
+        }
+    }
+
     /**
      * Returns whether the display list is currently usable. If this returns false,
      * the display list should be re-recorded prior to replaying it.
@@ -907,6 +922,7 @@
     @Override
     protected void finalize() throws Throwable {
         try {
+            destroyDisplayListData();
             nDestroyDisplayList(mNativeDisplayList);
         } finally {
             super.finalize();
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
index c90e4b0..81f778d 100644
--- a/core/java/android/view/GLRenderer.java
+++ b/core/java/android/view/GLRenderer.java
@@ -1196,10 +1196,10 @@
         }
     }
 
-    void swapDisplayListData(long displayList, long newData) {
-        nSwapDisplayListData(displayList, newData);
+    void setDisplayListData(long displayList, long newData) {
+        nSetDisplayListData(displayList, newData);
     }
-    private static native void nSwapDisplayListData(long displayList, long newData);
+    private static native void nSetDisplayListData(long displayList, long newData);
 
     private DisplayList buildDisplayList(View view, HardwareCanvas canvas) {
         if (mDrawDelta <= 0) {
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index c526dd2..46e2690 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -88,7 +88,7 @@
         }
 
         if (mDisplayList != null) {
-            mDisplayList.destroyDisplayListData(mRenderer);
+            mDisplayList.destroyDisplayListData();
             mDisplayList = null;
         }
         if (mRenderer != null) {
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index bcc28e3..34efcf5 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -562,7 +562,7 @@
         mRequested = requested;
     }
 
-    abstract void swapDisplayListData(long displayList, long newData);
+    abstract void setDisplayListData(long displayList, long newData);
 
     /**
      * Describes a series of frames that should be drawn on screen as a graph.
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 7e745d8..c183f08 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -628,11 +628,19 @@
     /** Key code constant: Brightness Up key.
      * Adjusts the screen brightness up. */
     public static final int KEYCODE_BRIGHTNESS_UP   = 221;
-    /** Key code constant: Audio Track key
+    /** Key code constant: Audio Track key.
      * Switches the audio tracks. */
     public static final int KEYCODE_MEDIA_AUDIO_TRACK = 222;
+    /** Key code constant: Sleep key.
+     * Puts the device to sleep.  Behaves somewhat like {@link #KEYCODE_POWER} but it
+     * has no effect if the device is already asleep. */
+    public static final int KEYCODE_SLEEP           = 223;
+    /** Key code constant: Wakeup key.
+     * Wakes up the device.  Behaves somewhat like {@link #KEYCODE_POWER} but it
+     * has no effect if the device is already awake. */
+    public static final int KEYCODE_WAKEUP          = 224;
 
-    private static final int LAST_KEYCODE           = KEYCODE_MEDIA_AUDIO_TRACK;
+    private static final int LAST_KEYCODE = KEYCODE_WAKEUP;
 
     // NOTE: If you add a new keycode here you must also add it to:
     //  isSystem()
@@ -877,6 +885,8 @@
         names.append(KEYCODE_BRIGHTNESS_DOWN, "KEYCODE_BRIGHTNESS_DOWN");
         names.append(KEYCODE_BRIGHTNESS_UP, "KEYCODE_BRIGHTNESS_UP");
         names.append(KEYCODE_MEDIA_AUDIO_TRACK, "KEYCODE_MEDIA_AUDIO_TRACK");
+        names.append(KEYCODE_SLEEP, "KEYCODE_SLEEP");
+        names.append(KEYCODE_WAKEUP, "KEYCODE_WAKEUP");
     };
 
     // Symbolic names of all metakeys in bit order from least significant to most significant.
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 3dcfbb3..a1fb123 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -148,8 +148,8 @@
     }
 
     @Override
-    void swapDisplayListData(long displayList, long newData) {
-        nSwapDisplayListData(mNativeProxy, displayList, newData);
+    void setDisplayListData(long displayList, long newData) {
+        nSetDisplayListData(mNativeProxy, displayList, newData);
     }
 
     @Override
@@ -257,7 +257,7 @@
     private static native boolean nInitialize(long nativeProxy, Surface window);
     private static native void nUpdateSurface(long nativeProxy, Surface window);
     private static native void nSetup(long nativeProxy, int width, int height);
-    private static native void nSwapDisplayListData(long nativeProxy, long displayList,
+    private static native void nSetDisplayListData(long nativeProxy, long displayList,
             long newData);
     private static native void nDrawDisplayList(long nativeProxy, long displayList,
             int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 827c4cc..d72f810 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8705,7 +8705,14 @@
      *
      * <p>When implementing this, you probably also want to implement
      * {@link #onCheckIsTextEditor()} to indicate you will return a
-     * non-null InputConnection.
+     * non-null InputConnection.</p>
+     *
+     * <p>Also, take good care to fill in the {@link android.view.inputmethod.EditorInfo}
+     * object correctly and in its entirety, so that the connected IME can rely
+     * on its values. For example, {@link android.view.inputmethod.EditorInfo#initialSelStart}
+     * and  {@link android.view.inputmethod.EditorInfo#initialSelEnd} members
+     * must be filled in with the correct cursor position for IMEs to work correctly
+     * with your application.</p>
      *
      * @param outAttrs Fill in with attribute information about the connection.
      */
@@ -10534,7 +10541,7 @@
      * by the layout system and should not generally be called otherwise, because the property
      * may be changed at any time by the layout.
      *
-     * @param left The bottom of this view, in pixels.
+     * @param left The left of this view, in pixels.
      */
     public final void setLeft(int left) {
         if (left != mLeft) {
@@ -10601,7 +10608,7 @@
      * by the layout system and should not generally be called otherwise, because the property
      * may be changed at any time by the layout.
      *
-     * @param right The bottom of this view, in pixels.
+     * @param right The right of this view, in pixels.
      */
     public final void setRight(int right) {
         if (right != mRight) {
@@ -10927,9 +10934,8 @@
      * Set to true to enable this View to cast shadows.
      * <p>
      * If enabled, and the View has a z translation greater than 0, or is
-     * rotated in 3D, the shadow will be cast onto the current
-     * {@link ViewGroup#setIsolatedZVolume(boolean) isolated Z volume},
-     * at the z = 0 plane.
+     * rotated in 3D, the shadow will be cast onto its parent at the z = 0
+     * plane.
      * <p>
      * The shape of the shadow being cast is defined by the
      * {@link #setOutline(Path) outline} of the view, or the rectangular bounds
@@ -14056,13 +14062,12 @@
     }
 
     private void resetDisplayList() {
-        HardwareRenderer renderer = getHardwareRenderer();
         if (mDisplayList != null && mDisplayList.isValid()) {
-            mDisplayList.destroyDisplayListData(renderer);
+            mDisplayList.destroyDisplayListData();
         }
 
         if (mBackgroundDisplayList != null && mBackgroundDisplayList.isValid()) {
-            mBackgroundDisplayList.destroyDisplayListData(renderer);
+            mBackgroundDisplayList.destroyDisplayListData();
         }
     }
 
@@ -17230,7 +17235,7 @@
             } else {
                 long value = mMeasureCache.valueAt(cacheIndex);
                 // Casting a long to int drops the high 32 bits, no mask needed
-                setMeasuredDimension((int) (value >> 32), (int) value);
+                setMeasuredDimensionRaw((int) (value >> 32), (int) value);
                 mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
             }
 
@@ -17325,6 +17330,22 @@
             measuredWidth  += optical ? opticalWidth  : -opticalWidth;
             measuredHeight += optical ? opticalHeight : -opticalHeight;
         }
+        setMeasuredDimensionRaw(measuredWidth, measuredHeight);
+    }
+
+    /**
+     * Sets the measured dimension without extra processing for things like optical bounds.
+     * Useful for reapplying consistent values that have already been cooked with adjustments
+     * for optical bounds, etc. such as those from the measurement cache.
+     *
+     * @param measuredWidth The measured width of this view.  May be a complex
+     * bit mask as defined by {@link #MEASURED_SIZE_MASK} and
+     * {@link #MEASURED_STATE_TOO_SMALL}.
+     * @param measuredHeight The measured height of this view.  May be a complex
+     * bit mask as defined by {@link #MEASURED_SIZE_MASK} and
+     * {@link #MEASURED_STATE_TOO_SMALL}.
+     */
+    private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
         mMeasuredWidth = measuredWidth;
         mMeasuredHeight = measuredHeight;
 
@@ -19137,7 +19158,18 @@
         }
 
         static int adjust(int measureSpec, int delta) {
-            return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec));
+            final int mode = getMode(measureSpec);
+            if (mode == UNSPECIFIED) {
+                // No need to adjust size for UNSPECIFIED mode.
+                return makeMeasureSpec(0, UNSPECIFIED);
+            }
+            int size = getSize(measureSpec) + delta;
+            if (size < 0) {
+                Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +
+                        ") spec: " + toString(measureSpec) + " delta: " + delta);
+                size = 0;
+            }
+            return makeMeasureSpec(size, mode);
         }
 
         /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9c50323..f9b9401 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3166,6 +3166,8 @@
      * @see #setIsolatedZVolume(boolean)
      *
      * @return True if the ViewGroup has an isolated Z volume.
+     *
+     * @hide
      */
     public boolean hasIsolatedZVolume() {
         return ((mGroupFlags & FLAG_ISOLATED_Z_VOLUME) != 0);
@@ -3180,6 +3182,8 @@
      *         ordering space, false if its decendents should inhabit the
      *         inherited Z ordering volume.
      * @attr ref android.R.styleable#ViewGroup_isolatedZVolume
+     *
+     * @hide
      */
     public void setIsolatedZVolume(boolean isolateZVolume) {
         boolean previousValue = (mGroupFlags & FLAG_ISOLATED_Z_VOLUME) != 0;
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index d4e005b..c0395cf 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -26,13 +26,13 @@
 /**
  * An EditorInfo describes several attributes of a text editing object
  * that an input method is communicating with (typically an EditText), most
- * importantly the type of text content it contains.
+ * importantly the type of text content it contains and the current cursor position.
  */
 public class EditorInfo implements InputType, Parcelable {
     /**
      * The content type of the text box, whose bits are defined by
      * {@link InputType}.
-     * 
+     *
      * @see InputType
      * @see #TYPE_MASK_CLASS
      * @see #TYPE_MASK_VARIATION
@@ -47,55 +47,55 @@
      * to provide alternative mechanisms for providing that command.
      */
     public static final int IME_MASK_ACTION = 0x000000ff;
-    
+
     /**
      * Bits of {@link #IME_MASK_ACTION}: no specific action has been
      * associated with this editor, let the editor come up with its own if
      * it can.
      */
     public static final int IME_ACTION_UNSPECIFIED = 0x00000000;
-    
+
     /**
      * Bits of {@link #IME_MASK_ACTION}: there is no available action.
      */
     public static final int IME_ACTION_NONE = 0x00000001;
-    
+
     /**
      * Bits of {@link #IME_MASK_ACTION}: the action key performs a "go"
      * operation to take the user to the target of the text they typed.
      * Typically used, for example, when entering a URL.
      */
     public static final int IME_ACTION_GO = 0x00000002;
-    
+
     /**
      * Bits of {@link #IME_MASK_ACTION}: the action key performs a "search"
      * operation, taking the user to the results of searching for the text
      * they have typed (in whatever context is appropriate).
      */
     public static final int IME_ACTION_SEARCH = 0x00000003;
-    
+
     /**
      * Bits of {@link #IME_MASK_ACTION}: the action key performs a "send"
      * operation, delivering the text to its target.  This is typically used
      * when composing a message in IM or SMS where sending is immediate.
      */
     public static final int IME_ACTION_SEND = 0x00000004;
-    
+
     /**
      * Bits of {@link #IME_MASK_ACTION}: the action key performs a "next"
      * operation, taking the user to the next field that will accept text.
      */
     public static final int IME_ACTION_NEXT = 0x00000005;
-    
+
     /**
      * Bits of {@link #IME_MASK_ACTION}: the action key performs a "done"
      * operation, typically meaning there is nothing more to input and the
      * IME will be closed.
      */
     public static final int IME_ACTION_DONE = 0x00000006;
-    
+
     /**
-     * Bits of {@link #IME_MASK_ACTION}: Like {@link #IME_ACTION_NEXT}, but
+     * Bits of {@link #IME_MASK_ACTION}: like {@link #IME_ACTION_NEXT}, but
      * for moving to the previous field.  This will normally not be used to
      * specify an action (since it precludes {@link #IME_ACTION_NEXT}), but
      * can be returned to the app if it sets {@link #IME_FLAG_NAVIGATE_PREVIOUS}.
@@ -154,7 +154,7 @@
      * on older versions of the platform.
      */
     public static final int IME_FLAG_NO_EXTRACT_UI = 0x10000000;
-    
+
     /**
      * Flag of {@link #imeOptions}: used in conjunction with one of the actions
      * masked by {@link #IME_MASK_ACTION}, this indicates that the action
@@ -167,7 +167,7 @@
      * to show more text.
      */
     public static final int IME_FLAG_NO_ACCESSORY_ACTION = 0x20000000;
-    
+
     /**
      * Flag of {@link #imeOptions}: used in conjunction with one of the actions
      * masked by {@link #IME_MASK_ACTION}. If this flag is not set, IMEs will
@@ -202,13 +202,13 @@
      * Generic unspecified type for {@link #imeOptions}.
      */
     public static final int IME_NULL = 0x00000000;
-    
+
     /**
      * Extended type information for the editor, to help the IME better
      * integrate with it.
      */
     public int imeOptions = IME_NULL;
-    
+
     /**
      * A string supplying additional information options that are
      * private to a particular IME implementation.  The string must be
@@ -221,7 +221,7 @@
      * attribute of a TextView.
      */
     public String privateImeOptions = null;
-    
+
     /**
      * In some cases an IME may be able to display an arbitrary label for
      * a command the user can perform, which you can specify here. This is
@@ -233,7 +233,7 @@
      * ignore this.
      */
     public CharSequence actionLabel = null;
-    
+
     /**
      * If {@link #actionLabel} has been given, this is the id for that command
      * when the user presses its button that is delivered back with
@@ -241,50 +241,66 @@
      * InputConnection.performEditorAction()}.
      */
     public int actionId = 0;
-    
+
     /**
      * The text offset of the start of the selection at the time editing
-     * began; -1 if not known. Keep in mind some IMEs may not be able
-     * to give their full feature set without knowing the cursor position;
-     * avoid passing -1 here if you can.
+     * begins; -1 if not known. Keep in mind that, without knowing the cursor
+     * position, many IMEs will not be able to offer their full feature set and
+     * may even behave in unpredictable ways: pass the actual cursor position
+     * here if possible at all.
+     *
+     * <p>Also, this needs to be the cursor position <strong>right now</strong>,
+     * not at some point in the past, even if input is starting in the same text field
+     * as before. When the app is filling this object, input is about to start by
+     * definition, and this value will override any value the app may have passed to
+     * {@link InputMethodManager#updateSelection(android.view.View, int, int, int, int)}
+     * before.</p>
      */
     public int initialSelStart = -1;
-    
+
     /**
-     * The text offset of the end of the selection at the time editing
-     * began; -1 if not known. Keep in mind some IMEs may not be able
-     * to give their full feature set without knowing the cursor position;
-     * avoid passing -1 here if you can.
+     * <p>The text offset of the end of the selection at the time editing
+     * begins; -1 if not known. Keep in mind that, without knowing the cursor
+     * position, many IMEs will not be able to offer their full feature set and
+     * may behave in unpredictable ways: pass the actual cursor position
+     * here if possible at all.</p>
+     *
+     * <p>Also, this needs to be the cursor position <strong>right now</strong>,
+     * not at some point in the past, even if input is starting in the same text field
+     * as before. When the app is filling this object, input is about to start by
+     * definition, and this value will override any value the app may have passed to
+     * {@link InputMethodManager#updateSelection(android.view.View, int, int, int, int)}
+     * before.</p>
      */
     public int initialSelEnd = -1;
-    
+
     /**
      * The capitalization mode of the first character being edited in the
      * text.  Values may be any combination of
      * {@link TextUtils#CAP_MODE_CHARACTERS TextUtils.CAP_MODE_CHARACTERS},
      * {@link TextUtils#CAP_MODE_WORDS TextUtils.CAP_MODE_WORDS}, and
      * {@link TextUtils#CAP_MODE_SENTENCES TextUtils.CAP_MODE_SENTENCES}, though
-     * you should generally just take a non-zero value to mean start out in
-     * caps mode.
+     * you should generally just take a non-zero value to mean "start out in
+     * caps mode".
      */
     public int initialCapsMode = 0;
-    
+
     /**
      * The "hint" text of the text view, typically shown in-line when the
      * text is empty to tell the user what to enter.
      */
     public CharSequence hintText;
-    
+
     /**
      * A label to show to the user describing the text they are writing.
      */
     public CharSequence label;
-    
+
     /**
      * Name of the package that owns this editor.
      */
     public String packageName;
-    
+
     /**
      * Identifier for the editor's field.  This is optional, and may be
      * 0.  By default it is filled in with the result of
@@ -292,14 +308,14 @@
      * is being edited.
      */
     public int fieldId;
-    
+
     /**
      * Additional name for the editor's field.  This can supply additional
      * name information for the field.  By default it is null.  The actual
      * contents have no meaning.
      */
     public String fieldName;
-    
+
     /**
      * Any extra data to supply to the input method.  This is for extended
      * communication with specific input methods; the name fields in the
@@ -309,7 +325,7 @@
      * attribute of a TextView.
      */
     public Bundle extras;
-    
+
     /**
      * Ensure that the data in this EditorInfo is compatible with an application
      * that was developed against the given target API version.  This can
@@ -365,10 +381,10 @@
                 + " fieldName=" + fieldName);
         pw.println(prefix + "extras=" + extras);
     }
-    
+
     /**
      * Used to package this object into a {@link Parcel}.
-     * 
+     *
      * @param dest The {@link Parcel} to be written.
      * @param flags The flags used for parceling.
      */
@@ -392,30 +408,31 @@
     /**
      * Used to make this class parcelable.
      */
-    public static final Parcelable.Creator<EditorInfo> CREATOR = new Parcelable.Creator<EditorInfo>() {
-        public EditorInfo createFromParcel(Parcel source) {
-            EditorInfo res = new EditorInfo();
-            res.inputType = source.readInt();
-            res.imeOptions = source.readInt();
-            res.privateImeOptions = source.readString();
-            res.actionLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
-            res.actionId = source.readInt();
-            res.initialSelStart = source.readInt();
-            res.initialSelEnd = source.readInt();
-            res.initialCapsMode = source.readInt();
-            res.hintText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
-            res.label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
-            res.packageName = source.readString();
-            res.fieldId = source.readInt();
-            res.fieldName = source.readString();
-            res.extras = source.readBundle();
-            return res;
-        }
+    public static final Parcelable.Creator<EditorInfo> CREATOR =
+            new Parcelable.Creator<EditorInfo>() {
+                public EditorInfo createFromParcel(Parcel source) {
+                    EditorInfo res = new EditorInfo();
+                    res.inputType = source.readInt();
+                    res.imeOptions = source.readInt();
+                    res.privateImeOptions = source.readString();
+                    res.actionLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+                    res.actionId = source.readInt();
+                    res.initialSelStart = source.readInt();
+                    res.initialSelEnd = source.readInt();
+                    res.initialCapsMode = source.readInt();
+                    res.hintText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+                    res.label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+                    res.packageName = source.readString();
+                    res.fieldId = source.readInt();
+                    res.fieldName = source.readString();
+                    res.extras = source.readBundle();
+                    return res;
+                }
 
-        public EditorInfo[] newArray(int size) {
-            return new EditorInfo[size];
-        }
-    };
+                public EditorInfo[] newArray(int size) {
+                    return new EditorInfo[size];
+                }
+            };
 
     public int describeContents() {
         return 0;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 70c53d2..e812edd 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1394,6 +1394,14 @@
     
     /**
      * Report the current selection range.
+     *
+     * <p><strong>Editor authors</strong>, you need to call this method whenever
+     * the cursor moves in your editor. Remember that in addition to doing this, your
+     * editor needs to always supply current cursor values in
+     * {@link EditorInfo#initialSelStart} and {@link EditorInfo#initialSelEnd} every
+     * time {@link android.view.View#onCreateInputConnection(EditorInfo)} is
+     * called, which happens whenever the keyboard shows up or the focus changes
+     * to a text field, among other cases.</p>
      */
     public void updateSelection(View view, int selStart, int selEnd,
             int candidatesStart, int candidatesEnd) {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 47d42dc..96a2ab5 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2135,15 +2135,15 @@
             mRecycler.markChildrenDirty();
         }
 
-        // TODO: Move somewhere sane. This doesn't belong in onLayout().
-        if (mFastScroll != null) {
-            mFastScroll.onItemCountChanged(childCount, mItemCount);
-        }
-
         layoutChildren();
         mInLayout = false;
 
         mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;
+
+        // TODO: Move somewhere sane. This doesn't belong in onLayout().
+        if (mFastScroll != null) {
+            mFastScroll.onItemCountChanged(getChildCount(), mItemCount);
+        }
     }
 
     /**
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 941cdd0..438a9da 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -296,7 +296,7 @@
         // The extra space for the thumb to move on the track
         available += mThumbOffset * 2;
 
-        int thumbPos = (int) (scale * available);
+        int thumbPos = (int) (scale * available + 0.5f);
 
         int topBound, bottomBound;
         if (gap == Integer.MIN_VALUE) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 98b43b3..53d9e28 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -287,13 +287,12 @@
     }
 
     private void destroyDisplayListsData() {
-        HardwareRenderer renderer = mTextView.getHardwareRenderer();
         if (mTextDisplayLists != null) {
             for (int i = 0; i < mTextDisplayLists.length; i++) {
                 DisplayList displayList = mTextDisplayLists[i] != null
                         ? mTextDisplayLists[i].displayList : null;
                 if (displayList != null && displayList.isValid()) {
-                    displayList.destroyDisplayListData(renderer);
+                    displayList.destroyDisplayListData();
                 }
             }
         }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 3f90f76..5ba5c57 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -41,6 +41,9 @@
 
     void noteStartWakelockFromSource(in WorkSource ws, int pid, String name, String historyName,
             int type, boolean unimportantForLogging);
+    void noteChangeWakelockFromSource(in WorkSource ws, int pid, String name, int type,
+            in WorkSource newWs, int newPid, String newName,
+            String newHistoryName, int newType, boolean newUnimportantForLogging);
     void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, int type);
 
     void noteVibratorOn(int uid, long durationMillis);
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index bc051ce..03a053c 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -504,6 +504,7 @@
 
         private String mEnabledInputMethodsStrCache;
         private int mCurrentUserId;
+        private int[] mRelatedUserIds = new int[0];
 
         private static void buildEnabledInputMethodsSettingString(
                 StringBuilder builder, Pair<String, ArrayList<String>> pair) {
@@ -536,6 +537,22 @@
             mCurrentUserId = userId;
         }
 
+        public void setRelatedUserIds(int[] relatedUserIds) {
+            synchronized (this) {
+                mRelatedUserIds = relatedUserIds;
+            }
+        }
+
+        public boolean isRelatedToOrCurrentUser(int userId) {
+            synchronized (this) {
+                if (userId == mCurrentUserId) return true;
+                for (int i = 0; i < mRelatedUserIds.length; i++) {
+                    if (userId == mRelatedUserIds[i]) return true;
+                }
+                return false;
+            }
+        }
+
         public List<InputMethodInfo> getEnabledInputMethodListLocked() {
             return createEnabledInputMethodListLocked(
                     getEnabledInputMethodsAndSubtypeListLocked());
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index fd93604..10fd2f0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -87,7 +87,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 97 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 99 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -1712,7 +1712,7 @@
     static final int DELTA_STATE_FLAG           = 0x00100000;
     // Flag in delta int: a new full state2 int follows.
     static final int DELTA_STATE2_FLAG          = 0x00200000;
-    // Flag in delta int: contains a wakelock tag.
+    // Flag in delta int: contains a wakelock or wakeReason tag.
     static final int DELTA_WAKELOCK_FLAG        = 0x00400000;
     // Flag in delta int: contains an event description.
     static final int DELTA_EVENT_FLAG           = 0x00800000;
@@ -1759,7 +1759,7 @@
         if (stateIntChanged) {
             firstToken |= DELTA_STATE_FLAG;
         }
-        if (cur.wakelockTag != null) {
+        if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
             firstToken |= DELTA_WAKELOCK_FLAG;
         }
         if (cur.eventCode != HistoryItem.EVENT_NONE) {
@@ -1795,11 +1795,24 @@
                     + " batteryPlugType=" + cur.batteryPlugType
                     + " states=0x" + Integer.toHexString(cur.states));
         }
-        if (cur.wakelockTag != null) {
-            int index = writeHistoryTag(cur.wakelockTag);
-            dest.writeInt(index);
-            if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
-                + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
+        if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
+            int wakeLockIndex;
+            int wakeReasonIndex;
+            if (cur.wakelockTag != null) {
+                wakeLockIndex = writeHistoryTag(cur.wakelockTag);
+                if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
+                    + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
+            } else {
+                wakeLockIndex = 0xffff;
+            }
+            if (cur.wakeReasonTag != null) {
+                wakeReasonIndex = writeHistoryTag(cur.wakeReasonTag);
+                if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
+                    + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
+            } else {
+                wakeReasonIndex = 0xffff;
+            }
+            dest.writeInt((wakeReasonIndex<<16) | wakeLockIndex);
         }
         if (cur.eventCode != HistoryItem.EVENT_NONE) {
             int index = writeHistoryTag(cur.eventTag);
@@ -1905,14 +1918,29 @@
         }
 
         if ((firstToken&DELTA_WAKELOCK_FLAG) != 0) {
-            cur.wakelockTag = cur.localWakelockTag;
-            int index = src.readInt();
-            readHistoryTag(index, cur.wakelockTag);
+            int indexes = src.readInt();
+            int wakeLockIndex = indexes&0xffff;
+            int wakeReasonIndex = (indexes>>16)&0xffff;
+            if (wakeLockIndex != 0xffff) {
+                cur.wakelockTag = cur.localWakelockTag;
+                readHistoryTag(wakeLockIndex, cur.wakelockTag);
+                if (DEBUG) Slog.i(TAG, "READ DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
+                    + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
+            } else {
+                cur.wakelockTag = null;
+            }
+            if (wakeReasonIndex != 0xffff) {
+                cur.wakeReasonTag = cur.localWakeReasonTag;
+                readHistoryTag(wakeReasonIndex, cur.wakeReasonTag);
+                if (DEBUG) Slog.i(TAG, "READ DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
+                    + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
+            } else {
+                cur.wakeReasonTag = null;
+            }
             cur.numReadInts += 1;
-            if (DEBUG) Slog.i(TAG, "READ DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
-                + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
         } else {
             cur.wakelockTag = null;
+            cur.wakeReasonTag = null;
         }
 
         if ((firstToken&DELTA_EVENT_FLAG) != 0) {
@@ -1944,6 +1972,7 @@
         if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
                 && timeDiff < 1000 && (diffStates&lastDiffStates) == 0
                 && (mHistoryLastWritten.wakelockTag == null || mHistoryCur.wakelockTag == null)
+                && (mHistoryLastWritten.wakeReasonTag == null || mHistoryCur.wakeReasonTag == null)
                 && (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
                         || mHistoryCur.eventCode == HistoryItem.EVENT_NONE)
                 && mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel
@@ -1968,6 +1997,13 @@
                 mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
                 mHistoryCur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
             }
+            // If the last written history had a wake reason tag, we need to retain it.
+            // Note that the condition above made sure that we aren't in a case where
+            // both it and the current history item have a wakelock tag.
+            if (mHistoryLastWritten.wakeReasonTag != null) {
+                mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
+                mHistoryCur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
+            }
             // If the last written history had an event, we need to retain it.
             // Note that the condition above made sure that we aren't in a case where
             // both it and the current history item have an event.
@@ -2016,6 +2052,7 @@
         writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
         mLastHistoryTime = curTime;
         mHistoryCur.wakelockTag = null;
+        mHistoryCur.wakeReasonTag = null;
         mHistoryCur.eventCode = HistoryItem.EVENT_NONE;
         mHistoryCur.eventTag = null;
         if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
@@ -2237,9 +2274,8 @@
     }
 
     public void noteStartWakeLocked(int uid, int pid, String name, String historyName, int type,
-            boolean unimportantForLogging) {
+            boolean unimportantForLogging, long elapsedRealtime) {
         uid = mapUid(uid);
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
         if (type == WAKE_TYPE_PARTIAL) {
             // Only care about partial wake locks, since full wake locks
             // will be canceled when the user puts the screen to sleep.
@@ -2271,9 +2307,8 @@
         }
     }
 
-    public void noteStopWakeLocked(int uid, int pid, String name, int type) {
+    public void noteStopWakeLocked(int uid, int pid, String name, int type, long elapsedRealtime) {
         uid = mapUid(uid);
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
         if (type == WAKE_TYPE_PARTIAL) {
             mWakeLockNesting--;
             if (mWakeLockNesting == 0) {
@@ -2291,19 +2326,50 @@
 
     public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name,
             String historyName, int type, boolean unimportantForLogging) {
-        int N = ws.size();
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final int N = ws.size();
         for (int i=0; i<N; i++) {
-            noteStartWakeLocked(ws.get(i), pid, name, historyName, type, unimportantForLogging);
+            noteStartWakeLocked(ws.get(i), pid, name, historyName, type, unimportantForLogging,
+                    elapsedRealtime);
+        }
+    }
+
+    public void noteChangeWakelockFromSourceLocked(WorkSource ws, int pid, String name, int type,
+            WorkSource newWs, int newPid, String newName,
+            String newHistoryName, int newType, boolean newUnimportantForLogging) {
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        // For correct semantics, we start the need worksources first, so that we won't
+        // make inappropriate history items as if all wake locks went away and new ones
+        // appeared.  This is okay because tracking of wake locks allows nesting.
+        final int NN = ws.size();
+        for (int i=0; i<NN; i++) {
+            noteStartWakeLocked(newWs.get(i), newPid, newName, newHistoryName, newType,
+                    newUnimportantForLogging, elapsedRealtime);
+        }
+        final int NO = ws.size();
+        for (int i=0; i<NO; i++) {
+            noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime);
         }
     }
 
     public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name, int type) {
-        int N = ws.size();
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final int N = ws.size();
         for (int i=0; i<N; i++) {
-            noteStopWakeLocked(ws.get(i), pid, name, type);
+            noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime);
         }
     }
 
+    public void noteWakeupReasonLocked(int irq, String reason) {
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason irq #" + irq + "\"" + reason +"\": "
+                + Integer.toHexString(mHistoryCur.states));
+        mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
+        mHistoryCur.wakeReasonTag.string = reason;
+        mHistoryCur.wakeReasonTag.uid = irq;
+        addHistoryRecordLocked(elapsedRealtime);
+    }
+
     public int startAddingCpuLocked() {
         mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
 
@@ -2419,7 +2485,7 @@
         if (u != null) {
             Uid.Pid p = u.mPids.get(pid);
             if (p != null) {
-                return p.mWakeSum + (p.mWakeStart != 0 ? (realtime - p.mWakeStart) : 0);
+                return p.mWakeSumMs + (p.mWakeNesting > 0 ? (realtime - p.mWakeStartMs) : 0);
             }
         }
         return 0;
@@ -2515,8 +2581,8 @@
 
             // Fake a wake lock, so we consider the device waked as long
             // as the screen is on.
-            noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false);
-            
+            noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false, elapsedRealtime);
+
             // Update discharge amounts.
             if (mOnBatteryInternal) {
                 updateDischargeScreenLevelsLocked(false, true);
@@ -2537,7 +2603,7 @@
                 mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
             }
 
-            noteStopWakeLocked(-1, -1, "screen", WAKE_TYPE_PARTIAL);
+            noteStopWakeLocked(-1, -1, "screen", WAKE_TYPE_PARTIAL, elapsedRealtime);
 
             updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), true,
                     SystemClock.uptimeMillis() * 1000, elapsedRealtime * 1000);
@@ -3952,10 +4018,12 @@
                 mProcessStats.clear();
             }
             if (mPids.size() > 0) {
-                for (int i=0; !active && i<mPids.size(); i++) {
+                for (int i=mPids.size()-1; i>=0; i--) {
                     Pid pid = mPids.valueAt(i);
-                    if (pid.mWakeStart != 0) {
+                    if (pid.mWakeNesting > 0) {
                         active = true;
+                    } else {
+                        mPids.removeAt(i);
                     }
                 }
             }
@@ -3977,8 +4045,6 @@
                 mPackageStats.clear();
             }
 
-            mPids.clear();
-
             if (!active) {
                 if (mWifiRunningTimer != null) {
                     mWifiRunningTimer.detach();
@@ -4020,6 +4086,7 @@
                         mNetworkPacketActivityCounters[i].detach();
                     }
                 }
+                mPids.clear();
             }
 
             return !active;
@@ -5257,8 +5324,8 @@
             }
             if (pid >= 0 && type == WAKE_TYPE_PARTIAL) {
                 Pid p = getPidStatsLocked(pid);
-                if (p.mWakeStart == 0) {
-                    p.mWakeStart = elapsedRealtimeMs;
+                if (p.mWakeNesting++ == 0) {
+                    p.mWakeStartMs = elapsedRealtimeMs;
                 }
             }
         }
@@ -5270,9 +5337,11 @@
             }
             if (pid >= 0 && type == WAKE_TYPE_PARTIAL) {
                 Pid p = mPids.get(pid);
-                if (p != null && p.mWakeStart != 0) {
-                    p.mWakeSum += elapsedRealtimeMs - p.mWakeStart;
-                    p.mWakeStart = 0;
+                if (p != null && p.mWakeNesting > 0) {
+                    if (p.mWakeNesting-- == 1) {
+                        p.mWakeSumMs += elapsedRealtimeMs - p.mWakeStartMs;
+                        p.mWakeStartMs = 0;
+                    }
                 }
             }
         }
@@ -5718,6 +5787,9 @@
             mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
                     + Integer.toHexString(mHistoryCur.states));
+            mHistoryCur.currentTime = System.currentTimeMillis();
+            addHistoryBufferLocked(mSecRealtime, HistoryItem.CMD_CURRENT_TIME);
+            mHistoryCur.currentTime = 0;
             addHistoryRecordLocked(mSecRealtime);
             mDischargeCurrentLevel = mDischargeUnplugLevel = level;
             if (mScreenOn) {
@@ -6367,11 +6439,16 @@
             Slog.e("BatteryStats", "Error reading battery statistics", e);
         }
 
-        long now = SystemClock.elapsedRealtime();
-        if (USE_OLD_HISTORY) {
-            addHistoryRecordLocked(now, HistoryItem.CMD_START);
+        if (mHistoryBuffer.dataPosition() > 0) {
+            long now = SystemClock.elapsedRealtime();
+            if (USE_OLD_HISTORY) {
+                addHistoryRecordLocked(now, HistoryItem.CMD_START);
+            }
+            addHistoryBufferLocked(now, HistoryItem.CMD_START);
+            mHistoryCur.currentTime = System.currentTimeMillis();
+            addHistoryBufferLocked(now, HistoryItem.CMD_CURRENT_TIME);
+            mHistoryCur.currentTime = 0;
         }
-        addHistoryBufferLocked(now, HistoryItem.CMD_START);
     }
 
     public int describeContents() {
@@ -7219,8 +7296,7 @@
         pullPendingStateUpdatesLocked();
     }
 
-    public void dumpLocked(Context context, PrintWriter pw, boolean isUnpluggedOnly, int reqUid,
-            boolean historyOnly) {
+    public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
         if (DEBUG) {
             pw.println("mOnBatteryTimeBase:");
             mOnBatteryTimeBase.dump(pw, "  ");
@@ -7264,6 +7340,6 @@
                 mBluetoothStateTimer[i].logState(pr, "  ");
             }
         }
-        super.dumpLocked(context, pw, isUnpluggedOnly, reqUid, historyOnly);
+        super.dumpLocked(context, pw, flags, reqUid, histStart);
     }
 }
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index 9fefd00..b479cb1 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -58,7 +58,9 @@
         PackageManager pm = context.getPackageManager();
         return pm.hasSystemFeature(PackageManager.FEATURE_SENSOR_ACCELEROMETER)
                 && pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_PORTRAIT)
-                && pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE);
+                && pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE)
+                && context.getResources().getBoolean(
+                        com.android.internal.R.bool.config_supportAutoRotation);
     }
 
     /**
@@ -184,6 +186,7 @@
      */
     public static abstract class RotationPolicyListener {
         final ContentObserver mObserver = new ContentObserver(new Handler()) {
+            @Override
             public void onChange(boolean selfChange, Uri uri) {
                 RotationPolicyListener.this.onChange();
             }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index f5c18f5..2d79491 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -35,7 +35,6 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.Slog;
 import android.view.IWindowManager;
 import android.view.View;
 import android.widget.Button;
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index ee9c18d..94c3f44 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -750,10 +750,11 @@
         mOptions.add(opt);
     }
 
-    // libart tolerates libdvm flags, but not vice versa, so only pass these if libart.
+    // libart tolerates libdvm flags, but not vice versa, so only pass some options if libart.
     property_get("persist.sys.dalvik.vm.lib.1", dalvikVmLibBuf, "libdvm.so");
-    if (strncmp(dalvikVmLibBuf, "libart", 6) == 0) {
+    bool libart = (strncmp(dalvikVmLibBuf, "libart", 6) == 0);
 
+    if (libart) {
         // Extra options for DexClassLoader.
         property_get("dalvik.vm.dex2oat-flags", dex2oatFlagsBuf, "");
         parseExtraOpts(dex2oatFlagsBuf, "-Xcompiler-option");
@@ -781,6 +782,42 @@
         mOptions.add(opt);
     }
 
+    /*
+     * Set profiler options
+     */
+    if (libart) {
+      char period[sizeof("-Xprofile-period:") + PROPERTY_VALUE_MAX];
+      char duration[sizeof("-Xprofile-duration:") + PROPERTY_VALUE_MAX];
+      char interval[sizeof("-Xprofile-interval:") + PROPERTY_VALUE_MAX];
+      char backoff[sizeof("-Xprofile-backoff:") + PROPERTY_VALUE_MAX];
+
+      // Number of seconds during profile runs.
+      strcpy(period, "-Xprofile-period:");
+      property_get("dalvik.vm.profile.period_secs", period+17, "10");
+      opt.optionString = period;
+      mOptions.add(opt);
+
+      // Length of each profile run (seconds).
+      strcpy(duration, "-Xprofile-duration:");
+      property_get("dalvik.vm.profile.duration_secs", duration+19, "30");
+      opt.optionString = duration;
+      mOptions.add(opt);
+
+
+      // Polling interval during profile run (microseconds).
+      strcpy(interval, "-Xprofile-interval:");
+      property_get("dalvik.vm.profile.interval_us", interval+19, "10000");
+      opt.optionString = interval;
+      mOptions.add(opt);
+
+      // Coefficient for period backoff.  The the period is multiplied
+      // by this value after each profile run.
+      strcpy(backoff, "-Xprofile-backoff:");
+      property_get("dalvik.vm.profile.backoff_coeff", backoff+18, "2.0");
+      opt.optionString = backoff;
+      mOptions.add(opt);
+    }
+
     initArgs.version = JNI_VERSION_1_4;
     initArgs.options = mOptions.editArray();
     initArgs.nOptions = mOptions.size();
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index e8feacb..2b9a5c4 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -107,17 +107,19 @@
     chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
     chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
 
+    int32_t* xDivs = chunk->getXDivs();
     for (int i = 0; i < chunk->numXDivs; i++) {
-        chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
-        if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
-            chunk->xDivs[i]++;
+        xDivs[i] = int32_t(xDivs[i] * scale + 0.5f);
+        if (i > 0 && xDivs[i] == xDivs[i - 1]) {
+            xDivs[i]++;
         }
     }
 
+    int32_t* yDivs = chunk->getYDivs();
     for (int i = 0; i < chunk->numYDivs; i++) {
-        chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
-        if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
-            chunk->yDivs[i]++;
+        yDivs[i] = int32_t(yDivs[i] * scale + 0.5f);
+        if (i > 0 && yDivs[i] == yDivs[i - 1]) {
+            yDivs[i]++;
         }
     }
 }
@@ -379,7 +381,7 @@
             return nullObjectReturn("primitive array == null");
         }
 
-        peeker.fPatch->serialize(array);
+        memcpy(array, peeker.fPatch, peeker.fPatchSize);
         env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
     }
 
diff --git a/core/jni/android/graphics/NinePatchImpl.cpp b/core/jni/android/graphics/NinePatchImpl.cpp
index d43c1be..1793208 100644
--- a/core/jni/android/graphics/NinePatchImpl.cpp
+++ b/core/jni/android/graphics/NinePatchImpl.cpp
@@ -115,13 +115,15 @@
         defaultPaint.setDither(true);
         paint = &defaultPaint;
     }
-    
+   
+    const int32_t* xDivs = chunk.getXDivs();
+    const int32_t* yDivs = chunk.getYDivs();
     // if our SkCanvas were back by GL we should enable this and draw this as
     // a mesh, which will be faster in most cases.
     if (false) {
         SkNinePatch::DrawMesh(canvas, bounds, bitmap,
-                              chunk.xDivs, chunk.numXDivs,
-                              chunk.yDivs, chunk.numYDivs,
+                              xDivs, chunk.numXDivs,
+                              yDivs, chunk.numYDivs,
                               paint);
         return;
     }
@@ -145,8 +147,8 @@
     if (gTrace) {
         ALOGV("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()));
         ALOGV("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height());
-        ALOGV("======== ninepatch xDivs [%d,%d]\n", chunk.xDivs[0], chunk.xDivs[1]);
-        ALOGV("======== ninepatch yDivs [%d,%d]\n", chunk.yDivs[0], chunk.yDivs[1]);
+        ALOGV("======== ninepatch xDivs [%d,%d]\n", xDivs[0], xDivs[1]);
+        ALOGV("======== ninepatch yDivs [%d,%d]\n", yDivs[0], yDivs[1]);
     }
 #endif
 
@@ -171,8 +173,8 @@
     SkRect      dst;
     SkIRect     src;
 
-    const int32_t x0 = chunk.xDivs[0];
-    const int32_t y0 = chunk.yDivs[0];
+    const int32_t x0 = xDivs[0];
+    const int32_t y0 = yDivs[0];
     const SkColor initColor = ((SkPaint*)paint)->getColor();
     const uint8_t numXDivs = chunk.numXDivs;
     const uint8_t numYDivs = chunk.numYDivs;
@@ -191,12 +193,12 @@
 
     int numStretchyXPixelsRemaining = 0;
     for (i = 0; i < numXDivs; i += 2) {
-        numStretchyXPixelsRemaining += chunk.xDivs[i + 1] - chunk.xDivs[i];
+        numStretchyXPixelsRemaining += xDivs[i + 1] - xDivs[i];
     }
     int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining;
     int numStretchyYPixelsRemaining = 0;
     for (i = 0; i < numYDivs; i += 2) {
-        numStretchyYPixelsRemaining += chunk.yDivs[i + 1] - chunk.yDivs[i];
+        numStretchyYPixelsRemaining += yDivs[i + 1] - yDivs[i];
     }
     int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining;
 
@@ -235,7 +237,7 @@
             src.fBottom = bitmapHeight;
             dst.fBottom = bounds.fBottom;
         } else {
-            src.fBottom = chunk.yDivs[j];
+            src.fBottom = yDivs[j];
             const int srcYSize = src.fBottom - src.fTop;
             if (yIsStretchable) {
                 dst.fBottom = dst.fTop + calculateStretch(bounds.fBottom, dst.fTop,
@@ -252,15 +254,16 @@
         xIsStretchable = initialXIsStretchable;
         // The initial xDiv and whether the first column is considered
         // stretchable or not depends on whether xDiv[0] was zero or not.
+        const uint32_t* colors = chunk.getColors();
         for (i = xIsStretchable ? 1 : 0;
               i <= numXDivs && src.fLeft < bitmapWidth;
               i++, xIsStretchable = !xIsStretchable) {
-            color = chunk.colors[colorIndex++];
+            color = colors[colorIndex++];
             if (i == numXDivs) {
                 src.fRight = bitmapWidth;
                 dst.fRight = bounds.fRight;
             } else {
-                src.fRight = chunk.xDivs[i];
+                src.fRight = xDivs[i];
                 if (dstRightsHaveBeenCached) {
                     dst.fRight = dstRights[i];
                 } else {
diff --git a/core/jni/android/graphics/NinePatchPeeker.cpp b/core/jni/android/graphics/NinePatchPeeker.cpp
index d3482da..5daa1ad 100644
--- a/core/jni/android/graphics/NinePatchPeeker.cpp
+++ b/core/jni/android/graphics/NinePatchPeeker.cpp
@@ -28,11 +28,11 @@
         // You have to copy the data because it is owned by the png reader
         Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
         memcpy(patchNew, patch, patchSize);
-        // this relies on deserialization being done in place
         Res_png_9patch::deserialize(patchNew);
         patchNew->fileToDevice();
         free(fPatch);
         fPatch = patchNew;
+        fPatchSize = patchSize;
         //printf("9patch: (%d,%d)-(%d,%d)\n",
         //       fPatch.sizeLeft, fPatch.sizeTop,
         //       fPatch.sizeRight, fPatch.sizeBottom);
diff --git a/core/jni/android/graphics/NinePatchPeeker.h b/core/jni/android/graphics/NinePatchPeeker.h
index 10d268a..2043862 100644
--- a/core/jni/android/graphics/NinePatchPeeker.h
+++ b/core/jni/android/graphics/NinePatchPeeker.h
@@ -29,6 +29,7 @@
         // the host lives longer than we do, so a raw ptr is safe
         fHost = host;
         fPatch = NULL;
+        fPatchSize = 0;
         fLayoutBounds = NULL;
     }
 
@@ -38,6 +39,7 @@
     }
 
     Res_png_9patch*  fPatch;
+    size_t fPatchSize;
     int    *fLayoutBounds;
 
     virtual bool peek(const char tag[], const void* data, size_t length);
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 7e958e7..a64d3ba 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -17,10 +17,12 @@
 
 #define LOG_TAG "AudioTrack-JNI"
 
-#include <jni.h>
 #include <JNIHelp.h>
+#include <JniConstants.h>
 #include <android_runtime/AndroidRuntime.h>
 
+#include "ScopedBytes.h"
+
 #include <utils/Log.h>
 #include <media/AudioSystem.h>
 #include <media/AudioTrack.h>
@@ -503,13 +505,13 @@
 }
 
 // ----------------------------------------------------------------------------
-jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data,
-                  jint offsetInBytes, jint sizeInBytes) {
+jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* data,
+                  jint offsetInBytes, jint sizeInBytes, bool blocking = true) {
     // give the data to the native AudioTrack object (the data starts at the offset)
     ssize_t written = 0;
     // regular write() or copy the data to the AudioTrack's shared memory?
     if (track->sharedBuffer() == 0) {
-        written = track->write(data + offsetInBytes, sizeInBytes);
+        written = track->write(data + offsetInBytes, sizeInBytes, blocking);
         // for compatibility with earlier behavior of write(), return 0 in this case
         if (written == (ssize_t) WOULD_BLOCK) {
             written = 0;
@@ -563,7 +565,8 @@
 static jint android_media_AudioTrack_write_byte(JNIEnv *env,  jobject thiz,
                                                   jbyteArray javaAudioData,
                                                   jint offsetInBytes, jint sizeInBytes,
-                                                  jint javaAudioFormat) {
+                                                  jint javaAudioFormat,
+                                                  jboolean isWriteBlocking) {
     //ALOGV("android_media_AudioTrack_write_byte(offset=%d, sizeInBytes=%d) called",
     //    offsetInBytes, sizeInBytes);
     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
@@ -590,7 +593,8 @@
         return 0;
     }
 
-    jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes);
+    jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes,
+            isWriteBlocking == JNI_TRUE /* blocking */);
 
     env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0);
 
@@ -601,6 +605,31 @@
 
 
 // ----------------------------------------------------------------------------
+static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env,  jobject thiz,
+        jbyteArray javaBytes, jint byteOffset, jint offsetInBytes, jint sizeInBytes,
+        jint javaAudioFormat, jboolean isWriteBlocking) {
+    //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
+    //    offsetInBytes, sizeInBytes);
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                "Unable to retrieve AudioTrack pointer for write()");
+        return 0;
+    }
+
+    ScopedBytesRO bytes(env, javaBytes);
+    if (bytes.get() == NULL) {
+        ALOGE("Error retrieving source of audio data to play, can't play");
+        return AUDIOTRACK_ERROR_BAD_VALUE;
+    }
+
+    jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get() + byteOffset, offsetInBytes,
+            sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
+
+    return written;
+}
+
+// ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_write_short(JNIEnv *env,  jobject thiz,
                                                   jshortArray javaAudioData,
                                                   jint offsetInShorts, jint sizeInShorts,
@@ -608,7 +637,8 @@
     jint written = android_media_AudioTrack_write_byte(env, thiz,
                                                  (jbyteArray) javaAudioData,
                                                  offsetInShorts*2, sizeInShorts*2,
-                                                 javaAudioFormat);
+                                                 javaAudioFormat,
+                                                 JNI_TRUE /*blocking write, legacy behavior*/);
     if (written > 0) {
         written /= 2;
     }
@@ -890,7 +920,10 @@
                                          (void *)android_media_AudioTrack_setup},
     {"native_finalize",      "()V",      (void *)android_media_AudioTrack_finalize},
     {"native_release",       "()V",      (void *)android_media_AudioTrack_release},
-    {"native_write_byte",    "([BIII)I", (void *)android_media_AudioTrack_write_byte},
+    {"native_write_byte",    "([BIIIZ)I",(void *)android_media_AudioTrack_write_byte},
+    {"native_write_native_bytes",
+                             "(Ljava/lang/Object;IIIIZ)I",
+                                         (void *)android_media_AudioTrack_write_native_bytes},
     {"native_write_short",   "([SIII)I", (void *)android_media_AudioTrack_write_short},
     {"native_setVolume",     "(FF)V",    (void *)android_media_AudioTrack_set_volume},
     {"native_get_native_frame_count",
diff --git a/core/jni/android_view_GLRenderer.cpp b/core/jni/android_view_GLRenderer.cpp
index 5ea8460..b7e795e 100644
--- a/core/jni/android_view_GLRenderer.cpp
+++ b/core/jni/android_view_GLRenderer.cpp
@@ -140,7 +140,7 @@
     LayerRenderer::destroyLayer(layer);
 }
 
-static void android_view_GLRenderer_swapDisplayListData(JNIEnv* env, jobject clazz,
+static void android_view_GLRenderer_setDisplayListData(JNIEnv* env, jobject clazz,
         jlong displayListPtr, jlong newDataPtr) {
     using namespace android::uirenderer;
     DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
@@ -178,7 +178,7 @@
 
     { "getSystemTime",         "()J",   (void*) android_view_GLRenderer_getSystemTime },
     { "nDestroyLayer",         "(J)V",  (void*) android_view_GLRenderer_destroyLayer },
-    { "nSwapDisplayListData",  "(JJ)V", (void*) android_view_GLRenderer_swapDisplayListData },
+    { "nSetDisplayListData",  "(JJ)V", (void*) android_view_GLRenderer_setDisplayListData },
 #endif
 
     { "setupShadersDiskCache", "(Ljava/lang/String;)V",
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 444c8be..2b20758 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -103,12 +103,12 @@
     proxy->setup(width, height);
 }
 
-static void android_view_ThreadedRenderer_swapDisplayListData(JNIEnv* env, jobject clazz,
+static void android_view_ThreadedRenderer_setDisplayListData(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jlong displayListPtr, jlong newDataPtr) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
     DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
     DisplayListData* newData = reinterpret_cast<DisplayListData*>(newDataPtr);
-    proxy->swapDisplayListData(displayList, newData);
+    proxy->setDisplayListData(displayList, newData);
 }
 
 static void android_view_ThreadedRenderer_drawDisplayList(JNIEnv* env, jobject clazz,
@@ -191,7 +191,7 @@
     { "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize },
     { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
     { "nSetup", "(JII)V", (void*) android_view_ThreadedRenderer_setup },
-    { "nSwapDisplayListData", "(JJJ)V", (void*) android_view_ThreadedRenderer_swapDisplayListData },
+    { "nSetDisplayListData", "(JJJ)V", (void*) android_view_ThreadedRenderer_setDisplayListData },
     { "nDrawDisplayList", "(JJIIII)V", (void*) android_view_ThreadedRenderer_drawDisplayList },
     { "nDestroyCanvas", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvas },
     { "nAttachFunctor", "(JJ)V", (void*) android_view_ThreadedRenderer_attachFunctor },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4c0ddeb..b99cb90 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2069,6 +2069,14 @@
         android:description="@string/permdesc_setPointerSpeed"
         android:protectionLevel="signature" />
 
+    <!-- Allows low-level access to setting input device calibration.
+         <p>Not for use by normal applications.
+         @hide -->
+    <permission android:name="android.permission.SET_INPUT_CALIBRATION"
+        android:label="@string/permlab_setInputCalibration"
+        android:description="@string/permdesc_setInputCalibration"
+        android:protectionLevel="signature" />
+
     <!-- Allows low-level access to setting the keyboard layout.
          <p>Not for use by third-party applications.
          @hide -->
diff --git a/core/res/res/drawable/btn_borderless_quantum_dark.xml b/core/res/res/drawable/btn_borderless_quantum_dark.xml
index e1bff4f..2c0d3b7 100644
--- a/core/res/res/drawable/btn_borderless_quantum_dark.xml
+++ b/core/res/res/drawable/btn_borderless_quantum_dark.xml
@@ -14,10 +14,6 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@color/transparent" />
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/btn_default_pressed_quantum_dark" />
-    </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/btn_default_pressed_quantum_dark"
+    android:mask="@drawable/btn_qntm_alpha" />
diff --git a/core/res/res/drawable/btn_borderless_quantum_light.xml b/core/res/res/drawable/btn_borderless_quantum_light.xml
index e7a95b1..2faec20 100644
--- a/core/res/res/drawable/btn_borderless_quantum_light.xml
+++ b/core/res/res/drawable/btn_borderless_quantum_light.xml
@@ -14,10 +14,6 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@color/transparent" />
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/btn_default_pressed_quantum_light" />
-    </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/btn_default_pressed_quantum_light"
+    android:mask="@drawable/btn_qntm_alpha" />
diff --git a/core/res/res/drawable/btn_color_quantum_dark.xml b/core/res/res/drawable/btn_color_quantum_dark.xml
index 5e44a78..0507755 100644
--- a/core/res/res/drawable/btn_color_quantum_dark.xml
+++ b/core/res/res/drawable/btn_color_quantum_dark.xml
@@ -14,21 +14,16 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <selector>
-            <item android:state_enabled="false">
-                <nine-patch android:src="@drawable/btn_qntm_alpha"
-                    android:tint="@color/btn_default_normal_quantum_light" />
-            </item>
-            <item>
-                <nine-patch android:src="@drawable/btn_qntm_alpha"
-                    android:tint="@color/theme_color_500" />
-            </item>
-        </selector>
-    </item>
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/theme_color_300" />
-    </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/theme_color_300">
+    <selector>
+        <item android:state_enabled="false">
+            <nine-patch android:src="@drawable/btn_qntm_alpha"
+                android:tint="@color/btn_default_normal_quantum_dark" />
+        </item>
+        <item>
+            <nine-patch android:src="@drawable/btn_qntm_alpha"
+                android:tint="@color/theme_color_500" />
+        </item>
+    </selector>
+</touch-feedback>
diff --git a/core/res/res/drawable/btn_color_quantum_light.xml b/core/res/res/drawable/btn_color_quantum_light.xml
index d6be958..9166b8d 100644
--- a/core/res/res/drawable/btn_color_quantum_light.xml
+++ b/core/res/res/drawable/btn_color_quantum_light.xml
@@ -14,21 +14,16 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <selector>
-            <item android:state_enabled="false">
-                <nine-patch android:src="@drawable/btn_qntm_alpha"
-                    android:tint="@color/btn_default_normal_quantum_dark" />
-            </item>
-            <item>
-                <nine-patch android:src="@drawable/btn_qntm_alpha"
-                    android:tint="@color/theme_color_500" />
-            </item>
-        </selector>
-    </item>
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/theme_color_700" />
-    </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/theme_color_700">
+    <selector>
+        <item android:state_enabled="false">
+            <nine-patch android:src="@drawable/btn_qntm_alpha"
+                android:tint="@color/btn_default_normal_quantum_dark" />
+        </item>
+        <item>
+            <nine-patch android:src="@drawable/btn_qntm_alpha"
+                android:tint="@color/theme_color_500" />
+        </item>
+    </selector>
+</touch-feedback>
diff --git a/core/res/res/drawable/btn_default_quantum_dark.xml b/core/res/res/drawable/btn_default_quantum_dark.xml
index 7f0cca8..29c3e24 100644
--- a/core/res/res/drawable/btn_default_quantum_dark.xml
+++ b/core/res/res/drawable/btn_default_quantum_dark.xml
@@ -14,13 +14,8 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/btn_default_normal_quantum_dark" />
-    </item>
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/btn_default_pressed_quantum_dark" />
-    </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/btn_default_pressed_quantum_dark">
+    <nine-patch android:src="@drawable/btn_qntm_alpha"
+        android:tint="@color/btn_default_normal_quantum_dark" />
+</touch-feedback>
diff --git a/core/res/res/drawable/btn_default_quantum_light.xml b/core/res/res/drawable/btn_default_quantum_light.xml
index e391a80..c6e828c 100644
--- a/core/res/res/drawable/btn_default_quantum_light.xml
+++ b/core/res/res/drawable/btn_default_quantum_light.xml
@@ -14,13 +14,8 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/btn_default_normal_quantum_light" />
-    </item>
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/btn_default_pressed_quantum_light" />
-    </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/btn_default_pressed_quantum_light">
+    <nine-patch android:src="@drawable/btn_qntm_alpha"
+        android:tint="@color/btn_default_normal_quantum_light" />
+</touch-feedback>
diff --git a/core/res/res/drawable/item_background_borderless_quantum_dark.xml b/core/res/res/drawable/item_background_borderless_quantum_dark.xml
index 1caee4e..3f32850 100644
--- a/core/res/res/drawable/item_background_borderless_quantum_dark.xml
+++ b/core/res/res/drawable/item_background_borderless_quantum_dark.xml
@@ -15,4 +15,4 @@
 -->
 
 <touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/lighter_gray" />
+    android:tint="@color/lighter_gray" />
diff --git a/core/res/res/drawable/item_background_borderless_quantum_light.xml b/core/res/res/drawable/item_background_borderless_quantum_light.xml
index ecf7dfb..09f6ae9 100644
--- a/core/res/res/drawable/item_background_borderless_quantum_light.xml
+++ b/core/res/res/drawable/item_background_borderless_quantum_light.xml
@@ -15,4 +15,4 @@
 -->
 
 <touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/darker_gray" />
+    android:tint="@color/darker_gray" />
diff --git a/core/res/res/drawable/item_background_quantum_dark.xml b/core/res/res/drawable/item_background_quantum_dark.xml
index 5ccaa8e..3f32850 100644
--- a/core/res/res/drawable/item_background_quantum_dark.xml
+++ b/core/res/res/drawable/item_background_quantum_dark.xml
@@ -14,7 +14,5 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@color/transparent" />
-    <item android:drawable="@color/lighter_gray" />
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/lighter_gray" />
diff --git a/core/res/res/drawable/item_background_quantum_light.xml b/core/res/res/drawable/item_background_quantum_light.xml
index f1453c5..09f6ae9 100644
--- a/core/res/res/drawable/item_background_quantum_light.xml
+++ b/core/res/res/drawable/item_background_quantum_light.xml
@@ -14,7 +14,5 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@color/transparent" />
-    <item android:drawable="@color/darker_gray" />
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/darker_gray" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 246bda1..0b9ac7f 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"deïnstalleer kortpaaie"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Laat die program toe om Tuisskerm-kortpaaie te verwyder sonder gebruikerinmenging."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"herlei uitgaande oproepe"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Laat die program toe om uitgaande oproepe te verwerk en die nommer wat geskakel moet word te verander. Hierdie toestemming laat die program toe om uitgaande oproepe te monitor, herlei, of te voorkom."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Laat die program toe om te sien watter nommer tydens \'n uitgaande oproep geskakel word, met die opsie om die oproep na \'n ander nommer te herlei of die oproep heeltemal te beëindig."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"ontvang teksboodskappe (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Laat die program toe om SMS-boodskappe te ontvang en te verwerk. Dit beteken dat die program boodskappe wat na jou toestel gestuur is, kan monitor of uitvee, sonder dat jy dit gesien het."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"ontvang teksboodskappe (MMS)"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index c92ecb3..5701852 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"አቋራጮችን ያራግፋል"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"መተግበሪያው ያለተጠቃሚ ጣልቃ-ገብነት የመነሻ ማያ ገጽ አቋራጮችን እንዲያስወግድ ያስችለዋል።"</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"የወጪ ጥሪዎች አቅጣጫ ቀይር"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"መተግበሪያው ወጪ ጥሪዎችን እንዲያስኬድና የሚደወለውን ቁጥር እንዲቀይር ይፈቅድለታል። ይህ ፈቃድ መተግበሪያው ወጪ ጥሪዎችን እንዲቆጣጠር፣ አቅጣጫ እንዲየስቀይር ወይም እንዲያግድ ይፈቅድለታል።"</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"በወጪ ጥሪ ጊዜ ጥሪውን ወደተለየ ቁጥር ከማዞር ወይም ጥሪውን በአጠቃላይ ከመተው አማራጭ ጋር እየተደወለለት ያለውን ቁጥር እንዲያይ ያስችለዋል።"</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"የፅሁፍ መልዕክቶችን ተቀበል (ኤስ.ኤም.ኤስ.)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"መተግበሪያው የኤስ.ኤም.ኤስ. መልእክቶችን እንዲያነብ እና እንዲያካሂድ ይፈቅዳል። ይህ ማለት መተግበሪያው ወደ መሳሪያህ የተላኩ መልእክቶችን ላንተ ሳያሳይህ ሊቆጣጠር ወይም ሊሰርዝ ይችላል።"</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"የፅሁፍ መልዕክቶችን ተቀበል (ኤም.ኤም.ኤስ.)"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 2c3549d..798ea7a 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"إزالة الاختصارات"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"للسماح للتطبيق بإزالة اختصارات من الشاشة الرئيسية بدون تدخل المستخدم."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"إعادة توجيه المكالمات الصادرة"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"للسماح للتطبيق بمعالجة المكالمات الصادرة وتغيير الرقم المطلوب. يتيح هذا الإذن للتطبيق مراقبة المكالمات الصادرة أو إعادة توجيهها أو منعها."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"للسماح للتطبيق بالاطلاع على الرقم الذي يتم الاتصال به عند إجراء مكالمة صادرة مع وجود الخيار لإعادة توجيه المكالمة إلى رقم آخر أو إنهاء المكالمة تمامًا."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"‏تلقي رسائل نصية (رسائل قصيرة SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"‏للسماح للتطبيق بتلقي ومعالجة الرسائل القصيرة SMS. وهذا يعني أنه يمكن للتطبيق مراقبة الرسائل التي يتم إرسالها إلى جهازك أو حذفها بدون عرضها لك."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"تلقي رسائل نصية (رسائل وسائط متعددة)"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index af7f9e8..f76ad7f 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"деинсталиране на преки пътища"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Разрешава на приложението да премахва преки пътища от началния екран без намеса на потребителя."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"пренасочване на изходящите обаждания"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Разрешава на приложението да обработва изходящите обаждания и да променя номера, който да се набере. Това разрешение му позволява да наблюдава, пренасочва или не допуска изходящи обаждания."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Разрешава на приложението да вижда набирания номер по време на изходящо обаждане и му дава възможност да пренасочи обаждането към друг номер или да го прекрати изцяло."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"получаване на текстови съобщения (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Разрешава на приложението да получава и обработва SMS съобщения. Това означава, че то може да наблюдава или изтрива изпратените до устройството ви, без да ви ги покаже."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"получаване на текстови съобщения (MMS)"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index cde9bdf..dc8128a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"desinstal·la dreceres"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Permet que l\'aplicació suprimeixi les dreceres de la pantalla d\'inici sense la intervenció de l\'usuari."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"desviació de les trucades sortints"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Permet que l\'aplicació processi les trucades sortints i que canviï el número que es marcarà. Aquest permís permet que l\'aplicació supervisi, redirigeixi o bloquegi les trucades sortints."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Permet que l\'aplicació vegi el número que s\'està marcant durant una trucada sortint, amb l\'opció de redirigir la trucada a un altre número o bé de cancel·lar-la completament."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"recepció de missatges de text (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Permet que l\'aplicació rebi i processi missatges SMS. Això vol dir que l\'aplicació pot controlar o suprimir missatges que s\'han enviat al teu dispositiu sense mostrar-te\'ls."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"recepció de missatges de text (MMS)"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 010f812..9745cdc 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"odinstalace zástupců"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Umožňuje aplikaci odebrat zástupce z plochy bez zásahu uživatele."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"přesměrování odchozích hovorů"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Umožňuje aplikaci zpracovávat odchozí hovory a měnit vytáčené číslo. Toto oprávnění umožňuje sledovat či přesměrovat odchozí hovory nebo jim zabránit."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Umožňuje aplikaci sledovat při odchozích hovorech volaná čísla a přesměrovat hovor na jiné číslo nebo jej zcela zrušit."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"příjem textových zpráv (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Umožňuje aplikaci přijmout a zpracovat zprávy SMS. Znamená to, že aplikace může sledovat zprávy odeslané do vašeho zařízení nebo je smazat, aniž by se vám zobrazily."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"příjem textových zpráv (MMS)"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 07474b7..ca12213 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"afinstaller genveje"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Tillader, at applikationen fjerner genveje på startskærmen uden brugerindgriben."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"omdirigere udgående opkald"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Tillader, at appen kan behandle udgående opkald og ændre det nummer, der skal ringes til. Med denne tilladelse kan appen overvåge, omdirigere eller forhindre udgående opkald."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Tillader, at appen kan se det nummer, der ringes op til under et udgående opkald, og giver mulighed for at omdirigere opkaldet til et andet nummer eller afbryde opkaldet helt."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"modtage tekstbeskeder (sms)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Tillader, at appen kan modtage og behandle sms-beskeder. Det betyder, at appen kan overvåge eller slette de beskeder, der sendes til din enhed, uden at vise dem til dig."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"modtage tekstbeskeder (mms)"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 5d2515f..404d4cc 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"Verknüpfungen deinstallieren"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Ermöglicht einer App das Entfernen von Verknüpfungen vom Startbildschirm ohne Eingriff des Nutzers"</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"Ausgehende Anrufe umleiten"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Ermöglicht der App, ausgehende Anrufe zu verarbeiten und die zu wählende Nummer zu ändern. Die Berechtigung erlaubt der App, ausgehende Anrufe zu überwachen, umzuleiten und zu unterbinden."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Ermöglicht der App die Erkennung der während eines ausgehenden Anrufs gewählten Nummer und gibt ihr die Möglichkeit, den Anruf an eine andere Nummer umzuleiten oder den Anruf ganz abzubrechen"</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"SMS empfangen"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Ermöglicht der App, SMS zu empfangen und zu verarbeiten. Das bedeutet, dass die App an Ihr Gerät gesendete Nachrichten überwachen und löschen kann, ohne sie Ihnen anzuzeigen."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"MMS empfangen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 8f297e1..1c3e185 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"κατάργηση εγκατάστασης συντομεύσεων"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Επιτρέπει στην εφαρμογή την κατάργηση συντομεύσεων από την Αρχική οθόνη χωρίς την παρέμβαση του χρήστη."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"αναδρομολόγηση εξερχόμενων κλήσεων"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Επιτρέπει στην εφαρμογή την επεξεργασία εξερχόμενων κλήσεων και την αλλαγή του αριθμού που πρόκειται να κληθεί. Αυτό δίνει τη δυνατότητα στην εφαρμογή να παρακολουθεί, να ανακατευθύνει ή να παρεμποδίζει εξερχόμενες κλήσεις:"</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Επιτρέπει στην εφαρμογή να βλέπει τον αριθμό που καλέσατε κατά τη διάρκεια μιας εξερχόμενης κλήσης με επιλογή ανακατεύθυνσης της κλήσης σε έναν διαφορετικό αριθμό ή διακοπής της κλήσης."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"λήψη μηνυμάτων κειμένου (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Επιτρέπει στην εφαρμογή τη λήψη και την επεξεργασία μηνυμάτων SMS. Αυτό σημαίνει ότι η εφαρμογή θα μπορούσε να παρακολουθήσει ή να διαγράψει τα μηνύματα που αποστέλλονται στη συσκευή σας χωρίς αυτά να εμφανιστούν σε εσάς."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"λήψη μηνυμάτων κειμένου (MMS)"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index c515849..c4b3a2b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"uninstall shortcuts"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Allows the application to remove Home screen shortcuts without user intervention."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"reroute outgoing calls"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Allows the app to process outgoing calls and change the number to be dialled. This permission allows the app to monitor, redirect or prevent outgoing calls."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Allows the app to see the number being dialled during an outgoing call with the option to redirect the call to a different number or abort the call altogether."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"receive text messages (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Allows the app to receive and process SMS messages. This means that the app could monitor or delete messages sent to your device without showing them to you."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"receive text messages (MMS)"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index c515849..c4b3a2b 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"uninstall shortcuts"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Allows the application to remove Home screen shortcuts without user intervention."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"reroute outgoing calls"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Allows the app to process outgoing calls and change the number to be dialled. This permission allows the app to monitor, redirect or prevent outgoing calls."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Allows the app to see the number being dialled during an outgoing call with the option to redirect the call to a different number or abort the call altogether."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"receive text messages (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Allows the app to receive and process SMS messages. This means that the app could monitor or delete messages sent to your device without showing them to you."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"receive text messages (MMS)"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 545b317..1446aff 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"desinstalar accesos directos"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Permite que la aplicación elimine accesos directos de la pantalla principal sin que el usuario intervenga."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"redireccionar llamadas salientes"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Permite que la aplicación procese las llamadas salientes y cambie el número que se va a marcar. La aplicación puede utilizar este permiso para controlar o desviar llamadas salientes o para impedir que se realicen."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Permite que la aplicación vea el número marcado al realizar una llamada, con la opción de redirigir esta llamada a un número distinto o cancelarla completamente."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"recibir mensajes de texto (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite que la aplicación reciba y procese mensajes SMS, lo que significa que podría controlar o eliminar mensajes enviados al dispositivo sin mostrártelos."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"recibir mensajes de texto (MMS)"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 515caca..6d4ee8e 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"desinstalar accesos directos"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Permite que la aplicación elimine accesos directos de la pantalla de inicio sin la intervención del usuario."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"redireccionar llamadas salientes"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Permite que la aplicación procese las llamadas salientes y cambie el número que se va a marcar. La aplicación puede utilizar este permiso para controlar, desviar o impedir que se realicen llamadas salientes."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Permite que la aplicación vea el número que se marca al realizar una llamada con la opción de redirigir la llamada a otro número o cancelar la llamada."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"recibir mensajes de texto (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite que la aplicación reciba y procese mensajes MMS, lo que significa que podría utilizar este permiso para controlar o eliminar mensajes enviados al dispositivo sin mostrárselos al usuario."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"recibir mensajes de texto (MMS)"</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 988ab47..629852f 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"otseteede desinstallimine"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Lubab rakendusel eemaldada avaekraani otseteid ilma kasutaja sekkumiseta."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"marsruutige väljuvad kõned uuesti"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Võimaldab rakendusel töödelda väljuvaid kõnesid ja muuta valitavat numbrit. Luba võimaldab rakendusel jälgida, ümber suunata või takistada väljuvaid kõnesid."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Lubab rakendusel näha, mis number valitakse väljahelistamisel, ning laseb suunata kõne teisele numbrile või selle üldse katkestada."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"võtke vastu tekstisõnumeid (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Võimaldab rakendusel vastu võtta ja töödelda SMS-sõnumeid. See tähendab, et rakendus võib jälgida või kustutada teie seadmele saadetud sõnumeid neid teile näitamata."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"võtke vastu tekstisõnumeid (MMS)"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 1f2a6cc..a73d136 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"حذف نصب میان‌برها"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"به برنامه اجازه می‌دهد میان‌برهای صفحه اصلی را بدون دخالت کاربر حذف کند."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"ترسیم مجدد مسیر تماس‌های خروجی"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"‏به برنامه اجازه می‎دهد تماس‌های خروجی را پردازش کند و شماره‎هایی که باید گرفته شوند را تغییر دهد. این مجوز به برنامه امکان می‌دهد به کنترل، هدایت مجدد یا جلوگیری از تماس‌های خروجی بپردازد."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"به برنامه اجازه می‌دهد عددی را که در طی یک تماس خروجی شماره‌گیری شده ببیند و این اختیار را دارد که تماس را به شماره دیگری هدایت کند یا کلاً تماس را قطع کند."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"دریافت پیام‌های نوشتاری (پیامک)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"به برنامه اجازه می‌دهد پیامک‌ها را دریافت و پردازش کند. این یعنی برنامه می‌تواند پیام‌های ارسالی به دستگاه شما را بدون نمایش آن‌ها به شما حذف یا کنترل کند."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"‏دریافت پیام‌های نوشتاری (MMS)"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index cbec12b..4e9ddd1 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"poista pikakuvakkeita"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Antaa sovelluksen poistaa aloitusruudun pikakuvakkeita ilman käyttäjän toimia."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"ohjaa uudelleen lähtevät puhelut"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Antaa sovelluksen käsitellä lähteviä puheluita ja muuttaa kohdenumeroita. Sovellus voi valvoa, uudelleenohjata tai estää lähteviä puheluita."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Sallii sovelluksen nähdä numeron, joka valitaan lähtevää puhelua soitettaessa, ja antaa mahdollisuuden ohjata puhelun eri numeroon tai keskeyttää puhelun kokonaan."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"vastaanota tekstiviestejä (teksti)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Antaa sovelluksen vastaanottaa ja käsitellä tekstiviestejä. Sovellus voi valvoa tai poistaa laitteeseesi lähetettyjä viestejä näyttämättä niitä sinulle."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"vastaanota tekstiviestejä (multimedia)"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 91749e6..79a1ab1 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"désinstaller des raccourcis"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Permet à l\'application de supprimer des raccourcis de la page d\'accueil sans intervention de l\'utilisateur."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"transférer les appels sortants"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Permet à l\'application de traiter les appels sortants et de modifier le numéro à composer. Cette autorisation lui permet de surveiller, rediriger ou empêcher les appels sortants."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Permet à l\'application de lire le numéro composé lors d\'un appel sortant et lui donne la possibilité de rediriger l\'appel vers un autre numéro ou d\'abandonner l\'appel."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"recevoir des messages texte"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Permet à l\'application de recevoir et de traiter les messages texte. Cette autorisation lui donne la possibilité de surveiller ou de supprimer les messages envoyés à votre appareil sans vous les montrer."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"recevoir des messages multimédias"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 51f0f76..f869af5 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"désinstaller des raccourcis"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Permettre à l\'application de supprimer des raccourcis de l\'écran d\'accueil sans l\'intervention de l\'utilisateur"</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"transférer les appels sortants"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Permet à l\'application de traiter les appels sortants et de modifier le numéro à composer. Cette autorisation lui permet de surveiller, rediriger ou empêcher les appels sortants."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Permettre à l\'application de lire le numéro composé lors d\'un appel sortant, et lui donner la possibilité de rediriger l\'appel vers un autre numéro ou d\'abandonner l\'appel"</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"recevoir des messages texte (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Permet à l\'application de recevoir et de traiter les SMS. Cette autorisation lui donne la possibilité de surveiller ou supprimer les messages envoyés à votre appareil sans vous les montrer."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"recevoir des messages texte (MMS)"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index b35c14f..00fdd7c 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"शॉर्टकट अनइंस्टॉल करें"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"एप्‍लिकेशन को उपयोगकर्ता के हस्‍तक्षेप के बिना होमस्‍क्रीन शॉर्टकट निकालने की अनुमति देता है."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"आउटगोइंग कॉल को कहीं और भेजें"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"ऐप्स को आउटगोइंग कॉल संसाधित करने और डायल किए जाने वाला नंबर बदलने देता है. यह अनुमति ऐप्स को आउटगोइंग कॉल की निगरानी करने, रीडायरेक्‍ट करने, या उन्‍हें रोकने देती है."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"ऐप्स को किसी कॉल को भिन्न नंबर पर रिडायरेक्ट करने या पूरी तरह से कॉल निरस्त करने के विकल्प के साथ आउटगोइंग कॉल के दौरान डायल किए जा रहे नंबर को देखने की अनुमति देती है."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"पाठ संदेश (SMS) प्राप्त करें"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"ऐप्स  को SMS संदेशों को प्राप्‍त और संसाधित करने देता है. इसका अर्थ है कि ऐप्स  आपके उपकरण पर भेजे गए संदेशों की निगरानी आपको दिखाए बिना कर सकता है और उन्‍हें हटा सकता है."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"पाठ संदेश (MMS) प्राप्त करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index b048bbc..6224106 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"deinstaliranje prečaca"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Aplikaciji omogućuje uklanjanje prečaca početnog zaslona bez intervencije korisnika."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"preusmjeravanje odlaznih poziva"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Aplikaciji omogućuje obradu odlaznih poziva i promjenu broja za biranje. Ta dozvola aplikaciji omogućuje nadziranje, preusmjeravanje ili sprječavanje odlaznih poziva."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Omogućuje aplikaciji da vidi broj koji se bira prilikom odlaznog poziva uz opciju preusmjeravanja poziva na neki drugi broj ili prekidanja poziva."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"primanje tekstnih poruka (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Aplikaciji omogućuje primanje i obradu SMS poruka. To znači da aplikacija može nadzirati ili izbrisati poruke poslane na vaš uređaj, a da vam ih ne prikaže."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"primanje tekstnih poruka (MMS)"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 1547900..c0f9c27 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"parancsikonok eltávolítása"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Lehetővé teszi egy alkalmazás számára, hogy felhasználói beavatkozás nélkül távolítson el parancsikonokat a kezdőképernyőről."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"kimenő hívások átirányítása"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Lehetővé teszi az alkalmazás számára a kimenő hívások kezdeményezését, és a tárcsázandó szám módosítását. Az engedéllyel rendelkező alkalmazás felügyelheti, átirányíthatja vagy megakadályozhatja a kimenő hívásokat."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Lehetővé teszi, hogy az alkalmazás kimenő híváskor lássa a tárcsázott számot, valamint a hívást átirányítsa egy másik számra, vagy megszakítsa."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"szöveges üzenetek (SMS) fogadása"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Lehetővé teszi az alkalmazás számára, hogy SMS-eket fogadjon és dolgozzon fel. Ez azt jelenti, hogy az alkalmazás megfigyelheti vagy törölheti a beérkező üzeneteket anélkül, hogy Ön látná azokat."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"szöveges üzenetek (MMS) fogadása"</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 77b8070..8a0f901 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"ապատեղադրել դյուրանցումները"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Հավելվածին թույլ է տալիս հեռացնել գլխավոր էկրանի դյուրանցումները՝ առանց օգտագործողի միջամտության:"</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"վերաուղղել ելքային զանգերը"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Թույլ է տալիս հավելվածին մշակել ելքային զանգերը և փոխել համարհավաքումը: Վնասարար հավելվածները կարող են վերահսկել, վերահասցեավորել կամ կանխել ելքային զանգերը:"</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Թույլ է տալիս ծրագրին ելքային զանգի ընթացքում տեսնել արդեն հավաքած համարը՝ հնարավորություն տալով վերահղել կամ անջատել զանգը։"</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"ստանալ տեքստային հաղորդագրություններ (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Թույլ է տալիս հավելվածին ստանալ և մշակել SMS հաղորդագրությունները: Սա նշանակում է, որ հավելվածը կարող է ստուգել կամ ջնջել ձեր սարքին ուղարկված հաղորդագրությունները` առանց դրանք ձեզ ցուցադրելու:"</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"ստանալ տեքստային հաղորդագրություններ (MMS)"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index de49cba..07e6cb2 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"mencopot pemasangan pintasan"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Mengizinkan aplikasi menghapus pintasan Layar Utama tanpa tindakan dari pengguna."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"ubah rute panggilan keluar"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Memungkinkan aplikasi memproses panggilan keluar dan mengubah nomor yang akan dipanggil. Izin ini memungkinkan aplikasi memantau, mengalihkan, atau mencegah panggilan keluar."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Memungkinkan aplikasi melihat nomor yang dihubungi saat melakukan panggilan keluar dengan opsi untuk mengalihkan panggilan ke nomor lain atau membatalkan panggilan sepenuhnya."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"terima pesan teks (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Memungkinkan aplikasi menerima dan memproses pesan SMS. Ini artinya aplikasi dapat memantau atau menghapus pesan yang dikirim ke perangkat Anda tanpa menunjukkannya kepada Anda."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"terima pesan teks (MMS)"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 808a3be..0f59bba 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"eliminazione di scorciatoie"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Consente all\'applicazione di rimuovere le scorciatoie della schermata Home automaticamente."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"reindirizzamento chiamate in uscita"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Consente all\'applicazione di elaborare le chiamate in uscita e di modificare il numero da comporre. Questa autorizzazione consente all\'applicazione di monitorare, reindirizzare o impedire le chiamate in uscita."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Consente all\'app di rilevare il numero digitato durante una chiamata in uscita, con la possibilità di reindirizzare la telefonata a un numero diverso o interromperla del tutto."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"ricezione messaggi di testo (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Consente all\'applicazione di ricevere ed elaborare messaggi SMS. Significa che l\'applicazione potrebbe monitorare o eliminare i messaggi inviati al tuo dispositivo senza mostrarteli."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"ricezione messaggi di testo (MMS)"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index d3e1cdd..128fbc7 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"הסר התקנה של קיצורי דרך"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"מאפשר לאפליקציה להסיר קיצורי דרך במסך דף הבית ללא התערבות המשתמש."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"ניתוב מחדש של שיחות יוצאות"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"מאפשר לאפליקציה לעבד שיחות יוצאות ולשנות את המספר שיש לחייג. אישור זה מאפשר לאפליקציה לעקוב אחר שיחות יוצאות, לבצע הפניה מחדש שלהן או אף למנוע את ביצוען."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"מאפשרת לאפליקציה לראות את המספר המחויג במהלך ביצוע שיחה יוצאת, עם האפשרות להפנות את השיחה למספר אחר או לבטל את השיחה לחלוטין."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"‏קבלת הודעות טקסט (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"‏מאפשר לאפליקציה לקבל ולעבד הודעות SMS. משמעות הדבר היא שהאפליקציה יכולה לעקוב אחר הודעות שנשלחו למכשיר או למחוק אותן מבלי להציג לך אותן."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"‏קבלת הודעות טקסט (MMS)"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 8f5d7fe..7892d69 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"ショートカットのアンインストール"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"ユーザー操作なしでホーム画面のショートカットを削除することをアプリに許可します。"</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"発信先の変更"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"通話の発信とダイヤルする番号の変更をアプリに許可します。これにより、アプリが発信を監視、転送、阻止できるようになります。"</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"発信を別の番号に転送するか完全に中止するオプションで、発信中にダイヤルされた番号にアクセスすることをアプリに許可します。"</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"テキストメッセージ(SMS)の受信"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"SMSメッセージの受信と処理をアプリに許可します。これにより、アプリが端末に届いたメッセージを表示することなく監視または削除できるようになります。"</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"テキストメッセージ(MMS)の受信"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 8bb038c..a48ac95 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"მალსახმობების წაშლა"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"მთავარ ეკრანზე აპლიკაციისთვის მალსახმობების დამოუკიდებლად წაშლის უფლების მიცემა."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"გამავალი ზარების გადამისამართება"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"აპს შეეძლება გამავალი ზარების დამუშავება და ასაკრეფი ნომრის შეცვლა. ეს უფლება აპს აძლევს შესაძლებლობას აკონტროლოს, გადაამისამართოს ან აღკვეთოს გამავალი ზარები."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"საშუალებას აძლევს აპს გამავალი ზარის დროს დაინახონ ზარის მიმღების ნომერი, ზარის სხვა მისამართზე გადამისამართებით ან ზარის საერთოდ შეწყვეტის საშუალებით."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"ტექსტური შეტყობინებების (SMS) მიღება"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"აპს შეეძლება SMS შეტყობინებების მიღება და დამუშავება. ეს ნიშნავს, რომ აპს შეეძლება თქვენ მოწყობილობაზე გამოგზავნილი შეტყობინებების მონიტორინგი და მათი წაშლა თქვენთვის ჩვენების გარეშე."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"ტექსტური შეტყობინებების (MMS) მიღება"</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 1d93e2e..488a439 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"លុប​ផ្លូវកាត់"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"ឲ្យ​កម្មវិធី​លុប​ផ្លូវកាត់​អេក្រង់​ដើម​ដោយ​គ្មាន​អំពើ​ពី​អ្នកប្រើ។"</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"នាំ​ផ្លូវ​ការ​ហៅ​ចេញ​ឡើងវិញ"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"ឲ្យ​កម្មវិធី​ដំណើរការ​ការ​ហៅ​ចេញ និង​ប្ដូរ​លេខ​ត្រូវ​ហៅ។ សិទ្ធិ​នេះ​អនុញ្ញាត​ឲ្យ​កម្មវិធី​តាមដាន ប្ដូរ​ទិស ឬ​ការពារ​ការ​ហៅ​ចេញ។"</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"ឲ្យ​កម្មវិធី​ឃើញ​លេខ​ដែល​កំពុង​ត្រូវ​បាន​ហៅ​អំឡុង​ពេល​ហៅ​ចេញ​​ដោយ​ប្រើ​​ជម្រើស​ ដើម្បី​បញ្ជូន​​ការ​ហៅ​បន្ត​ទៅ​លេខ​ផ្សេង ឬ​បោះបង់​ការ​ហៅ​រួម​គ្នា។"</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"ទទួល​សារ​អត្ថបទ (សារ SMS​)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"ឲ្យ​កម្មវិធី​ទទួល និង​ដំណើរការ​​សារ MMS ។ មាន​ន័យ​ថា កម្មវិធី​អាច​ត្រួតពិនិត្យ​ ឬ​លុប​សារ​ដែល​បាន​ផ្ញើ​ទៅ​ឧបករណ៍​របស់​អ្នក ដោយ​​មិន​បង្ហាញ​អ្នក។"</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"ទទួល​សារ​អត្ថបទ (MMS​)"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 03542c5..681aabf 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"바로가기 제거"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"애플리케이션이 사용자의 작업 없이 홈 화면 바로가기를 삭제할 수 있도록 허용합니다."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"발신전화 경로 전환"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"앱이 발신 전화를 처리하고 전화를 걸 번호를 변경할 수 있도록 허용합니다. 이 권한을 사용하면 앱이 발신 전화를 모니터링, 리디렉션 또는 차단할 수도 있습니다."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"통화를 다른 번호로 리디렉션하거나 통화를 완전히 중단하는 옵션을 사용하여, 앱에서 발신 통화 중에 전화를 거는 번호를 볼 수 있게 허용합니다."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"문자 메시지 받기(SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"앱이 SMS 메시지를 수신하고 처리할 수 있도록 허용합니다. 이는 앱이 사용자에게 표시하지 않고 기기로 전송된 메시지를 모니터링 또는 삭제할 수도 있다는 것을 의미합니다."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"문자 메시지 받기(MMS)"</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 4c90dbe..64dcbc6 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"ຖອນທາງລັດ"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນລຶບທາງລັດໃນໜ້າຫຼັກໄດ້ ໂດຍບໍ່ຕ້ອງຮັບການຢືນຢັນຈາກຜູ່ໃຊ້."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"ປ່ຽນເສັ້ນທາງການໂທອອກ"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"ອະນຸຍາດໃຫ້ແອັບຯປະມວນຜົນສາຍທີ່ໂທອອກ ແລະປ່ຽນໝາຍເລກທີ່ຈະໂທອອກ. ແອັບຯທີ່ເປັນອັນຕະລາຍອາດກວດສອບ, ໂອນສາຍ ຫຼືຂັດຂວາງບໍ່ໃຫ້ໂທອອກໄດ້."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"ອະ​ນຸ​ຍາດ​ໃຫ້​ແອັບຯ​ເບິ່ງ​​ໝາຍ​ເລກເບີ​ໂທ ໃນ​ລະ​ຫວ່າງ​ການ​ໂທ​ອອກ ພ້ອມ​ທັງ​ໂຕ​ເລືອກ​ໃນ​ການ​ປ່ຽນ​ເສັ້ນ​ທາງ​ການ​ໂທ​ໄປ​ຫາ​ເບີ​ອື່ນ ຫຼື ລາຍ​ລະ​ອຽດກ່ຽວ​ກັບ​ເບີ​ໂທ​ລະ​ສັບ."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"ຮັບຂໍ້ຄວາມສັ້ນ (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"ອະນຸຍາດໃຫ້ແອັບຯຮັບ ແລະປະມວນຜົນຂໍ້ຄວາມ SMS. ນີ້ໝາຍຄວາມວ່າແອັບຯສາມາດຕິດຕາມ ຫຼືລຶບຂໍ້ຄວາມທີ່ສົ່ງເຂົ້າອຸປະກອນຂອງທ່ານ ໂດຍທີ່ບໍ່ສະແດງພວກມັນໃຫ້ທ່ານເຫັນ."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"ຮັບຂໍ້ຄວາມ (MMS)"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index a8cb3d2..b379738 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"pašalinti sparčiuosius klavišus"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Programai leidžiama pašalinti sparčiuosius klavišus iš pagrindinio ekrano be naudotojo įsikišimo."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"peradresuoti išsiunčiamuosius skambučius"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Leidžiama programai atlikti išsiunčiamuosius skambučius ir keisti renkamą numerį. Šis leidimas suteikia teisę programai stebėti, peradresuoti ar neleisti išsiunčiamųjų skambučių."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Leidžiama programai peržiūrėti renkamą numerį išsiunčiamojo skambučio metu suteikiant galimybę peradresuoti skambutį kitu numeriu arba visiškai nutraukti skambutį."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"gauti teksto pranešimus (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Leidžiama programai gauti ir apdoroti SMS pranešimus. Tai reiškia, kad programa gali stebėti ir ištrinti į jūsų įrenginį siunčiamus pranešimus jums jų neparodžiusi."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"gauti teksto pranešimus (MMS)"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 33dd64f..278b0d7 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"atinstalēt saīsnes"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Ļauj lietojumprogrammai noņemt saīsnes no sākuma ekrāna, nejautājot lietotājam."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"pārmaršrutēt izejošos zvanus"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Ļauj lietotnei apstrādāt izejošos zvanus un mainīt numuru, uz kuru tiks zvanīts. Ar šo atļauju lietotne var pārraudzīt, novirzīt vai neatļaut izejošos zvanus."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Ļauj lietotnei skatīt ievadīto tālruņa numuru izejošā zvana laikā un piedāvā iespēju šo zvanu pāradresēt uz citu numuru vai vispār pārtraukt zvanu."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"saņemt īsziņas (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Ļauj lietotnei saņemt un apstrādāt īsziņas. Tas nozīmē, ka lietotne var pārraudzīt vai dzēst uz jūsu ierīci nosūtītos ziņojumus, neparādot tos jums."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"saņemt ziņojumus (MMS)"</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 1590063..3d0b3cb 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"товчлолыг устгах"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Аппликешн нь хэрэглэгчийн оролцоогүйгээр Нүүр дэлгэцний товчлолыг устгаж чадна."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"гарсан дуудлагыг чиглэлийг өөрчлөх"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Апп нь дуудлага хийх болон залгаж байгаа дугаарыг өөрчлөх боломжтой. Энэ зөвшөөрөл нь апп-г залгасан дуудлагыг хаах, хянах болон дахин чиглүүлэх боломжтой болгодог."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Гадагш дуудлага хийх үед залгасан дугаарыг харах, дуудлагыг өөр дугаар руу шилжүүлэх, таслах боломжтой болгоно."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"текст мессеж(SMS) хүлээж авах"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Апп нь SMS мессежийг хүлээн авах болон гүйцэтгэх боломжтой. Ингэснээр апп нь таны төхөөрөмжрүү илгээсэн мессежийг танд үзүүлэхгүйгээр хянах болон устгаж чадна."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"текст мессеж(МMS) хүлээж авах"</string>
@@ -963,7 +963,7 @@
     <string name="permlab_setAlarm" msgid="1379294556362091814">"сэрүүлэг тохируулах"</string>
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Апп нь суулгагдсан сэрүүлэгний апп дээр сэрүүлэг тохируулах боломжтой. Зарим сэрүүлэгний апп нь энэ функцийг дэмжихгүй байж болзошгүй."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"дуут шуудан нэмэх"</string>
-    <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Апп нь таны дуут шуудангийн ирсэн мэйлд мессеж нэмэх боломжтой."</string>
+    <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Таны дуут шуудангийн ирсэн мэйлд зурвас нэмэхийг апп-д зөвшөөрөх."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Хөтчийн геобайршлын зөвшөөрлийг өөрчлөх"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Апп нь Хөтчийн гео байршлын зөвшөөрлийг өөрчлөх боломжтой. Хортой апп нь энийг ашиглан дурын веб хуудасруу байршлын мэдээллийг илгээх боломжтой."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"багцийг тулгах"</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index d5382ab..37d0d0f 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"nyahpasang pintasan"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Membenarkan aplikasi mengalih keluar pintasan Skrin Laman Utama tanpa campur tangan pengguna."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"tukar laluan panggilan keluar"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Membenarkan apl memproses panggilan keluar dan menukar nombor yang perlu didail. Kebenaran ini membolehkan apl memantau, mengalih atau menghalang panggilan keluar."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Membenarkan apl melihat nombor yang didail semasa panggilan keluar dengan pilihan untuk mengubah hala panggilan ke nombor lain atau membatalkan terus panggilan."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"terima mesej teks (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Membenarkan apl menerima dan memproses mesej SMS. Ini bermakna apl boleh memantau atau memadam mesej yang dihantar ke peranti anda tanpa menunjukkannya kepada anda."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"terima mesej teks (MMS)"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 6eae123..78df5e8 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"avinstallere snarveier"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Lar appen fjerne snarveier på startsiden uten å involvere brukeren."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"omdirigere utgående anrop"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Lar appen behandle utgående anrop og endre nummeret som skal ringes opp. Denne tillatelsen lar appen overvåke, viderekoble eller hindre utgående anrop."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Lar appen se nummeret det ringes til under en utgående samtale, med mulighet for å omdirigere anropet til et annet nummer eller avbryte samtalen fullstendig."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"motta tekstmeldinger (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Lar appen motta og behandle tekstmeldinger. Dette betyr at appen kan overvåke eller slette meldinger som er sendt til enheten din uten at du har sett dem."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"motta tekstmeldinger (MMS)"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 09497dc..8eaebf1 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"snelkoppelingen verwijderen"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"De app toestaan snelkoppelingen van het startscherm te verwijderen zonder tussenkomst van de gebruiker."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"uitgaande oproepen doorschakelen"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Hiermee kan de app uitgaande oproepen verwerken en het nummer wijzigen dat wordt gebeld. De app kan uitgaande oproepen bijhouden, omleiden of blokkeren."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"De app toestaan het nummer te bekijken dat wordt gekozen voor een uitgaande oproep, met de mogelijkheid de oproep om te leiden naar een ander nummer of de oproep helemaal af te breken."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"tekstberichten (SMS) ontvangen"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Hiermee kan de app sms-berichten ontvangen en verwerken. Dit betekent dat de app berichten die naar uw apparaat zijn verzonden, kan bijhouden of verwijderen zonder deze aan u weer te geven."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"tekstberichten (MMS) ontvangen"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 37dd42d..77d5441 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"odinstalowywanie skrótów"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Pozwala aplikacji usuwać skróty z ekranu głównego bez interwencji użytkownika."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"przekierowywanie połączeń wychodzących"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Pozwala aplikacji na przetwarzanie połączeń wychodzących i zmianę wybieranego numeru. Aplikacje z tym uprawnieniem mogą monitorować, przekierowywać lub blokować połączenia wychodzące."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Pozwala aplikacji na sprawdzenie numeru wybieranego w trakcie połączenia wychodzącego, a także umożliwia przerwanie połączenia lub przekierowanie go pod inny numer."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"odbieranie wiadomości tekstowych (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Pozwala aplikacji na odbieranie i przetwarzanie SMS-ów. To oznacza, że aplikacja będzie mogła bez Twojej wiedzy monitorować i usuwać wiadomości wysyłane do Twojego urządzenia."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"odbieranie wiadomości tekstowych (MMS)"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 6eb6cb2..e92961d 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"desinstalar atalhos"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Permite que a aplicação remova atalhos do Ecrã principal sem a intervenção do utilizador."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"redirecionar as chamadas efetuadas"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Permite que a aplicação processe chamadas efetuadas e mude o número a marcar. Esta autorização permite que a aplicação monitorize, redirecione ou impeça a realização de chamadas."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Permite que a aplicação veja o número que é marcado durante uma chamada efetuada, com a opção de redirecionar a chamada para um número diferente ou terminar a chamada."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"receber mensagens de texto (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite que a aplicação receba e processe mensagens SMS. Isto significa que a aplicação poderá monitorizar ou eliminar mensagens enviadas para o seu dispositivo sem as apresentar."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"receber mensagens de texto (MMS)"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 3a5b131..b3481b8 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"desinstalar atalhos"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Permite que o aplicativo remova atalhos da tela inicial sem a intervenção do usuário."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"redirecionar as chamadas efetuadas"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Permite que o aplicativo processe as chamadas de saída e altere o número a ser discado. Esta permissão autoriza o aplicativo a monitorar, redirecionar ou evitar chamadas de saída."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Permite que o aplicativo veja o número discado ao realizar uma chamada, com a opção de redirecionar a chamada para outro número ou abortá-la."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"receber mensagens de texto (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite que o aplicativo receba e processe mensagens SMS. Isso significa que o aplicativo pode monitorar ou excluir mensagens enviadas para o dispositivo sem mostrá-las para você."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"receber mensagens de texto (MMS)"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index f252f50..9c2c793 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -363,7 +363,7 @@
     <skip />
     <!-- no translation found for permlab_processOutgoingCalls (3906007831192990946) -->
     <skip />
-    <!-- no translation found for permdesc_processOutgoingCalls (5331318931937402040) -->
+    <!-- no translation found for permdesc_processOutgoingCalls (5156385005547315876) -->
     <skip />
     <!-- no translation found for permlab_receiveSms (8673471768947895082) -->
     <skip />
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 7610c06..016cb16 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"dezinstalează comenzi rapide"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Permite aplicației să elimine comenzi rapide de pe ecranul de pornire, fără intervenția utilizatorului."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"redirecţionează apelurile efectuate"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Permite aplicaţiei să proceseze apelurile efectuate şi să schimbe numărul care trebuie format. Cu această permisiune aplicaţia poate monitoriza, redirecţiona sau împiedica apelurile efectuate."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Permite aplicației să vadă numărul format în timpul unui apel de ieșire, cu opțiunea de a redirecționa apelul către un alt număr sau de a întrerupe apelul."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"primeşte mesaje text (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite aplicaţiei să primească şi să proceseze mesaje SMS. Acest lucru înseamnă că aplicaţia ar putea monitoriza sau şterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"primeşte mesaje text (MMS)"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 2ffc435..5c9e697 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"Удаление ярлыков"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Приложение сможет удалять ярлыки с главного экрана без вмешательства пользователя."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"Перенаправление исходящих вызовов"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Приложение сможет обрабатывать исходящие вызовы и изменять набираемые номера, а также отслеживать, перенаправлять или блокировать исходящие вызовы."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Приложение сможет видеть набранный номер во время исходящего вызова и при необходимости перенаправлять вызов или завершать его."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"Прием SMS-сообщений"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Приложение сможет получать и обрабатывать SMS. Это значит, что оно сможет отслеживать и удалять отправленные на ваше устройство сообщения, не показывая их."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"Прием MMS-сообщений"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index d3934a9..039e1eb 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"odinštalovať odkazy"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Povoľuje aplikácii odstrániť odkazy na ploche bez zásahu používateľa."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"presmerovať odchádzajúce hovory"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Umožňuje aplikácii spracovávať odchádzajúce hovory a meniť vytáčané číslo. Toto povolenie umožňuje aplikácii sledovať a presmerovať odchádzajúce hovory alebo im zabrániť."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Umožňuje aplikácii počas odchádzajúceho hovoru rozpoznať vytáčané číslo a poskytuje možnosť presmerovať daný hovor na odlišné číslo alebo ho úplne zrušiť."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"prijímať textové správy (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Umožňuje aplikácii prijímať a spracovávať správy SMS. Znamená to, že aplikácia môže sledovať správy odoslané na vaše zariadenie alebo ich odstrániť bez toho, aby sa vám zobrazili."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"prijímať textové správy (MMS)"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 783b0a4..6a909d6 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"odstranjevanje bližnjic"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Aplikaciji omogoča odstranjevanje bližnjic z začetnega zaslona brez posredovanja uporabnika."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"preusmeritev odhodnih klicev"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Aplikaciji omogoča, da obdela odhodne klice in spreminja klicne številke. S tem lahko aplikacija nadzira, preusmerja ali preprečuje odhodne klice."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Aplikaciji dovoli ogled klicane številke pri odhodnem klicu in ji omogoča preusmeritev klica na drugo številko ali prekinitev klica."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"prejemanje sporočil (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Aplikaciji omogoča prejemanje in obdelavo SMS-ov. S tem lahko aplikacija nadzoruje ali izbriše sporočila, poslana v napravo, ne da bi vam jih pokazala."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"prejemanje sporočil (MMS)"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index fb12e96..9a53a2a 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"деинсталирање пречица"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Омогућава апликацији да уклања пречице са почетног екрана без интервенције корисника."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"преусмеравање одлазних позива"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Дозвољава апликацији да обрађује одлазне позиве и промени број који се бира. Ова дозвола омогућава апликацији да надгледа, преусмерава или спречава одлазне позиве."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Дозвољава апликацији да види који број се бира при одлазном позиву уз опцију да преусмери позив на други број или га потпуно прекине."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"пријем текстуалних порука (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Дозвољава апликацији да прима и обрађује SMS поруке. То значи да апликација може да надгледа или брише поруке које се шаљу уређају, а да вам их не прикаже."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"пријем текстуалних порука (MMS)"</string>
@@ -1236,7 +1236,7 @@
     <string name="sim_removed_message" msgid="2333164559970958645">"Мобилна мрежа неће бити доступна док не покренете систем поново уз уметање важеће SIM картице."</string>
     <string name="sim_done_button" msgid="827949989369963775">"Готово"</string>
     <string name="sim_added_title" msgid="3719670512889674693">"SIM картица је додата"</string>
-    <string name="sim_added_message" msgid="6599945301141050216">"Поново покрените уређај да бисте могли да приступите мобилној мрежи."</string>
+    <string name="sim_added_message" msgid="6599945301141050216">"Рестартујте уређај да бисте могли да приступите мобилној мрежи."</string>
     <string name="sim_restart_button" msgid="4722407842815232347">"Поново покрени"</string>
     <string name="time_picker_dialog_title" msgid="8349362623068819295">"Подешавање времена"</string>
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Подешавање датума"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index d6f7457..2e2d3f2 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"avinstallera genvägar"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Tillåter att appen tar bort genvägar på startskärmen utan åtgärd från användaren."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"omdirigera utgående samtal"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Tillåter att appen hanterar utgående samtal och ändrar numret som ska ringas upp. Med den här behörigheten kan appen övervaka, omdirigera eller förhindra utgående samtal."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Tillåter att appen ser numret du slår under ett utgående samtal och har möjlighet att koppla samtalet till ett annat nummer eller avbryta samtalet helt."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"ta emot textmeddelanden (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Tillåter att appen tar emot och hanterar SMS. Detta innebär att appen kan övervaka eller ta bort meddelanden som skickats till enheten utan att visa dem för dig."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"ta emot textmeddelanden (MMS)"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 7f17c76..5b15a7f 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"ondoa njia za mikato"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Huruhusu programu kuondoa njia za mkato za Skrini ya kwanza bila mtumiaji kuingilia."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"panga upya simu zinazotoka"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Inaruhusu programu kuchakata simu zinazotoka nje na kubadilisha nambari ya kupigwa. Idhini hii inaruhusu programu kuchunguza, kuelekeza upya, au kuzuia simu zinazotoka nje."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Huruhusu programu kuona nambari inayopigwa wakati simu inapigwa ikiwa na chaguo la kuelekeza simu kwa nambari tofauti au kukata simu kabisa."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"pokea ujumbe wa maandishi wa SMS"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Inaruhusu programu kupokea na kuchakata ujumbe wa SMS. Hii inamaanisha programu hii inaweza kuchunguza na kufuta ujumbe uliotumwa katika kifaa chako bila ya kukuonyesha."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"pokea ujumbe wa maandishi wa MMS"</string>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index d21f9b7..8f83ab2 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -22,6 +22,10 @@
     <dimen name="thumbnail_width">200dp</dimen>
     <!-- The height that is used when creating thumbnails of applications. -->
     <dimen name="thumbnail_height">177dp</dimen>
+    <!-- The width that is used when creating thumbnails of applications. -->
+    <dimen name="recents_thumbnail_width">512dp</dimen>
+    <!-- The height that is used when creating thumbnails of applications. -->
+    <dimen name="recents_thumbnail_height">512dp</dimen>
     <!-- The maximum number of action buttons that should be permitted within
          an action bar/action mode. This will be used to determine how many
          showAsAction="ifRoom" items can fit. "always" items can override this. -->
diff --git a/core/res/res/values-sw720dp/dimens.xml b/core/res/res/values-sw720dp/dimens.xml
index ccdb4be..040bb5b 100644
--- a/core/res/res/values-sw720dp/dimens.xml
+++ b/core/res/res/values-sw720dp/dimens.xml
@@ -38,6 +38,10 @@
     <dimen name="thumbnail_width">230dp</dimen>
     <!-- The height that is used when creating thumbnails of applications. -->
     <dimen name="thumbnail_height">135dp</dimen>
+    <!-- The width that is used when creating thumbnails of applications. -->
+    <dimen name="recents_thumbnail_width">512dp</dimen>
+    <!-- The height that is used when creating thumbnails of applications. -->
+    <dimen name="recents_thumbnail_height">512dp</dimen>
 
     <!-- Preference activity, vertical padding for the header list -->
     <dimen name="preference_screen_header_vertical_padding">32dp</dimen>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index f59b2d1..a4c328e 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"ถอนการติดตั้งทางลัด"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"อนุญาตให้แอปพลิเคชันลบทางลัดหน้าจอหลักโดยไม่ต้องให้ผู้ใช้จัดการ"</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"จัดเส้นทางการโทรออกใหม่"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"อนุญาตให้แอปพลิเคชันประมวลผลการโทรออกและเปลี่ยนแปลงหมายเลขที่จะโทรไป การอนุญาตนี้จะทำให้แอปพลิเคชันสามารถตรวจสอบ เปลี่ยนเส้นทาง หรือกีดขวางไม่ให้โทรออกได้"</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"อนุญาตให้แอปดูหมายเลขที่โทรในระหว่างการโทรออกโดยสามารถเลือกเปลี่ยนเส้นทางการโทรไปยังหมายเลขอื่นหรือยกเลิกการโทรไปเลย"</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"รับข้อความ (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"อนุญาตให้แอปพลิเคชันรับและประมวลผลข้อความ SMS ซึ่งหมายความว่าแอปพลิเคชันจะสามารถตรวจสอบหรือลบข้อความที่ส่งมายังอุปกรณ์ของคุณได้โดยไม่ต้องแสดงให้คุณเห็น"</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"รับข้อความ (MMS)"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 0f5f53c..4c54352 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"i-uninstall ang mga shortcut"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Pinapayagan ang application na alisin ang mga shortcut ng Homescreen nang walang panghihimasok ng user."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"baguhin ang ruta ng mga papalabas na tawag"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Pinapayagan ang app na magproseso ng mga papalabas na tawag at baguhin ang numerong ida-dial. Pinapayagan ng pahintulot na ito ang app na sumubaybay, mag-redirect, o pumigil ng mga papalabas na tawag."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Pinapayagan ang app na makita ang numerong idina-dial sa isang papalabas na tawag na may opsyon na i-redirect ang tawag sa ibang numero o itigil ang tawag nang tuluyan."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"tumanggap ng mga text message (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Pinapayagan ang app na tumanggap at magproseso ng mga mensaheng SMS. Nangangahulugan ito na maaaring sumubaybay o magtanggal ang app ng mga mensaheng ipinapadala sa iyong device nang hindi ipinapakita ang mga ito sa iyo."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"tumanggap ng mga text message (MMS)"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 35b1241..bf01a4e 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"kısayolların yüklemesini kaldırma"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Uygulamaya, kullanıcının müdahalesi olmadan kısayolları Ana Ekrandan kaldırma izni verir."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"giden çağrıları yeniden yönlendir"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Uygulamaya, yapılan çağrıları işleme ve aranacak numarayı değiştirme izni verir. Bu izin, uygulamanın yapılan çağrıları izlemesine, yönlendirmesine ve önlemesine olanak sağlar."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Uygulamaya, giden bir çağrının numarası çevrilirken çağrıyı farklı bir numaraya yönlendirme ya da tamamen kapatma seçeneğiyle birlikte numarayı görme izni verir."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"kısa mesajları al (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Uygulamaya SMS mesajlarını alma ve işleme izni verir. Bu izin, uygulamanın cihazınıza gönderilen mesajları takip edip size göstermeden silebileceği anlamına gelir."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"kısa mesajları (MMS) al"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 3a5f184..dc79da7 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"видаляти ярлики"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Дозволяє програмі самостійно вилучати ярлики з головного екрана."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"переадресовувати вихідні виклики"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Дозволяє програмі обробляти вихідні дзвінки та змінювати номер для виклику. Такий дозвіл дає програмі змогу відстежувати, переадресовувати чи блокувати вихідні дзвінки."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Дозволяє додатку читати номер вихідного дзвінка, переспрямовувати дзвінок на інший номер або переривати його."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"отримувати текстові повідомлення (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Дозволяє програмі отримувати й обробляти SMS-повідомлення. Це означає, що програма може відстежувати чи видаляти повідомлення, надіслані на ваш пристрій, навіть не показуючи їх вам."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"отримувати текстові повідомлення (MMS)"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 4b5e48a..5dcf41d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"gỡ cài đặt lối tắt"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Cho phép ứng dụng xóa lối tắt trên Màn hình chính mà không cần sự can thiệp của người dùng."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"định tuyến lại cuộc gọi đi"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Cho phép ứng dụng xử lý cuộc gọi đi và thay đổi số được gọi. Quyền này cho phép ứng dụng theo dõi, chuyển hướng hoặc chặn cuộc gọi đi."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Cho phép ứng dụng xem số được quay trong một cuộc gọi đi với tùy chọn chuyển hướng cuộc gọi đến một số khác hoặc hủy cuộc gọi đó hoàn toàn."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"nhận tin nhắn văn bản (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Cho phép ứng dụng nhận và xử lý tin nhắn SMS. Điều này có nghĩa là ứng dụng có thể theo dõi hoặc xóa tin nhắn được gửi đến thiết bị của bạn mà không hiển thị chúng cho bạn."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"nhận tin nhắn văn bản (MMS)"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index ebf11c4..5a9c285 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"卸载快捷方式"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"允许应用自行删除主屏幕快捷方式。"</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"重新设置外拨电话的路径"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"允许该应用处理外拨电话以及更改要拨打的号码。此权限可让该应用监视、重定向或阻止外拨电话。"</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"允许应用在拨出电话时查看拨打的电话号码,并选择改为拨打其他号码或完全中止通话。"</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"接收讯息(短信)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"允许该应用接收和处理短信。这就意味着,该应用可能会监视发送到您设备的短信,或删除发送到您设备的短信而不向您显示。"</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"接收讯息(彩信)"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 303affa..bfffe13 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"解除安裝捷徑"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"允許應用程式繞過用戶授權直接移除主畫面捷徑。"</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"重新設定撥出電話的路徑"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"允許應用程式處理撥出電話及更改撥打的號碼。這項權限允許應用程式監控、轉接或阻止撥出的電話。"</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"允許應用程式在撥出電話時查看所撥打的電話號碼,並選擇將電話重新導向至另一個號碼或完全中斷通話。"</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"接收短訊 (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"允許應用程式接收和處理短訊。這表示應用程式可監控傳送至您裝置的訊息,或在您閱讀訊息前擅自刪除訊息。"</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"接收短訊 (MMS)"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 544fd5b..d6bcb61 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"解除安裝捷徑"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"允許應用程式自動移除主螢幕捷徑。"</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"重設撥號路徑"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"允許應用程式處理撥出電話及更改撥打的號碼。這項權限可讓應用程式監控、轉接或阻止撥出的電話。"</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"允許應用程式在撥打電話期間查看撥出的電話號碼,並選擇改撥其他號碼或完全中斷通話。"</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"接收簡訊 (SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"允許應用程式接收和處理簡訊。這項設定可讓應用程式監控傳送至您裝置的訊息,或在您閱讀訊息前擅自刪除訊息。"</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"接收簡訊 (MMS)"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 802fc0f..a44c04c 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -256,7 +256,7 @@
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"khipha izinqamuleli"</string>
     <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Ivumela uhlelo lokusebenza ukususa izinqamuleli zesikrini sasekhaya ngaphandle kokungenela komsebenzisi."</string>
     <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"thumela amakholi aphumayo kabusha"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="5331318931937402040">"Ivumela uhlelo lokusebenza ukucubungula amakholi aphumayo futhi ishintshe inombolo ezoshayelwa. Le mvume ivumela uhlelo lokusebenza ukwengamela, liqondise kabusha, noma livikele amakholi aphumayo."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Ivumela uhlelo lokusebenza ukubona inombolo eshayelwayo ngesikhathi sekholi ephumayo ngenketho yokuqondisa kabusha ikholi kwinombolo ehlukile noma ukuyekisa ikholi yonke."</string>
     <string name="permlab_receiveSms" msgid="8673471768947895082">"thola imiyalezo ebhaliwe (i-SMS)"</string>
     <string name="permdesc_receiveSms" msgid="6424387754228766939">"Ivumela uhlelo lokusebenza ukuthola nokucubungula imilayezo ye-SMS. Loku kuchaza ukuthi uhlelo lokusebenza lungangamela noma lesuse imilayezo ethunyelwe kudivayisi yakho ngaphandle kokukubonisa yona."</string>
     <string name="permlab_receiveMms" msgid="1821317344668257098">"thola imiyalezo ebhaliwe (i-MMS)"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a78ce02..2043960 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1596,6 +1596,8 @@
         <enum name="KEYCODE_BRIGHTNESS_DOWN" value="220" />
         <enum name="KEYCODE_BRIGHTNESS_UP" value="221" />
         <enum name="KEYCODE_MEDIA_AUDIO_TRACK" value="222" />
+        <enum name="KEYCODE_MEDIA_SLEEP" value="223" />
+        <enum name="KEYCODE_MEDIA_WAKEUP" value="224" />
     </attr>
 
     <!-- ***************************************************************** -->
@@ -4427,6 +4429,20 @@
         <attr name="color" />
     </declare-styleable>
 
+    <!-- Drawable used to show animated touch feedback. -->
+    <declare-styleable name="TouchFeedbackDrawable">
+        <!-- The tint to use for feedback ripples. This attribute is mandatory. -->
+        <attr name="tint" />
+        <!-- Specifies the Porter-Duff blending mode used to apply the tint. The default vlaue is src_atop, which draws over the opaque parts of the drawable. -->
+        <attr name="tintMode" />
+        <!-- Whether to pin feedback ripples to the center of the drawable. Default value is false. -->
+        <attr name="pinned" format="boolean" />
+        <!-- Optional drawable used to mask ripple bounds before projection. -->
+        <attr name="mask" format="reference" />
+        <!-- Optional drawable onto which ripples are projected. -->
+        <attr name="drawable" />
+    </declare-styleable>
+
     <declare-styleable name="ScaleDrawable">
         <!-- Scale width, expressed as a percentage of the drawable's bound. The value's
              format is XX%. For instance: 100%, 12.5%, etc.-->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8d68277..cfd4a63 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -370,6 +370,17 @@
     <!-- If this is true, key chords can be used to take a screenshot on the device. -->
     <bool name="config_enableScreenshotChord">true</bool>
 
+    <!-- Auto-rotation behavior -->
+
+    <!-- If true, enables auto-rotation features using the accelerometer.
+         Otherwise, auto-rotation is disabled.  Applications may still request
+         to use specific orientations but the sensor is ignored and sensor-based
+         orientations are not available.  Furthermore, all auto-rotation related
+         settings are omitted from the system UI.  In certain situations we may
+         still use the accelerometer to determine the orientation, such as when
+         docked if the dock is configured to enable the accelerometer. -->
+    <bool name="config_supportAutoRotation">true</bool>
+
     <!-- If true, the screen can be rotated via the accelerometer in all 4
          rotations as the default behavior. -->
     <bool name="config_allowAllRotations">false</bool>
@@ -382,57 +393,12 @@
          true here reverses that logic. -->
     <bool name="config_reverseDefaultRotation">false</bool>
 
+    <!-- Lid switch behavior -->
+
     <!-- The number of degrees to rotate the display when the keyboard is open.
          A value of -1 means no change in orientation by default. -->
     <integer name="config_lidOpenRotation">-1</integer>
 
-    <!-- The number of degrees to rotate the display when the device is in a desk dock.
-         A value of -1 means no change in orientation by default. -->
-    <integer name="config_deskDockRotation">-1</integer>
-
-    <!-- The number of degrees to rotate the display when the device is in a car dock.
-         A value of -1 means no change in orientation by default. -->
-    <integer name="config_carDockRotation">-1</integer>
-
-    <!-- The number of degrees to rotate the display when the device has HDMI connected
-         but is not in a dock.  A value of -1 means no change in orientation by default.
-         Use -1 except on older devices whose Hardware Composer HAL does not
-         provide full support for multiple displays.  -->
-    <integer name="config_undockedHdmiRotation">-1</integer>
-
-    <!-- Control the default UI mode type to use when there is no other type override
-         happening.  One of the following values (See Configuration.java):
-             1  UI_MODE_TYPE_NORMAL
-             4  UI_MODE_TYPE_TELEVISION
-             5  UI_MODE_TYPE_APPLIANCE
-         Any other values will have surprising consequences. -->
-    <integer name="config_defaultUiModeType">1</integer>
-
-    <!-- Control whether being in the desk dock (and powered) always
-         keeps the screen on.  By default it stays on when plugged in to
-         AC.  0 will not keep it on; or together 1 to stay on when plugged
-         in to AC and 2 to stay on when plugged in to USB.  (So 3 for both.) -->
-    <integer name="config_deskDockKeepsScreenOn">1</integer>
-
-    <!-- Control whether being in the car dock (and powered) always
-         keeps the screen on.  By default it stays on when plugged in to
-         AC.  0 will not keep it on; or together 1 to stay on when plugged
-         in to AC and 2 to stay on when plugged in to USB.  (So 3 for both.) -->
-    <integer name="config_carDockKeepsScreenOn">1</integer>
-
-    <!-- Control whether being in the desk dock should enable accelerometer
-         based screen orientation.  This defaults to true because it is
-         common for desk docks to be sold in a variety of form factors
-         with different orientations.  Since we cannot always tell these docks
-         apart and the docks cannot report their true orientation on their own,
-         we rely on gravity to determine the effective orientation. -->
-    <bool name="config_deskDockEnablesAccelerometer">true</bool>
-
-    <!-- Control whether being in the car dock should enable accelerometer based
-         screen orientation.  This defaults to true because putting a device in
-         a car dock make the accelerometer more a physical input (like a lid). -->
-    <bool name="config_carDockEnablesAccelerometer">true</bool>
-
     <!-- Indicate whether the lid state impacts the accessibility of
          the physical keyboard.  0 means it doesn't, 1 means it is accessible
          when the lid is open, 2 means it is accessible when the lid is
@@ -450,6 +416,60 @@
          The default is false. -->
     <bool name="config_lidControlsSleep">false</bool>
 
+    <!-- Desk dock behavior -->
+
+    <!-- The number of degrees to rotate the display when the device is in a desk dock.
+         A value of -1 means no change in orientation by default. -->
+    <integer name="config_deskDockRotation">-1</integer>
+
+    <!-- Control whether being in the desk dock (and powered) always
+         keeps the screen on.  By default it stays on when plugged in to
+         AC.  0 will not keep it on; or together 1 to stay on when plugged
+         in to AC and 2 to stay on when plugged in to USB.  (So 3 for both.) -->
+    <integer name="config_deskDockKeepsScreenOn">1</integer>
+
+    <!-- Control whether being in the desk dock should enable accelerometer
+         based screen orientation.  This defaults to true because it is
+         common for desk docks to be sold in a variety of form factors
+         with different orientations.  Since we cannot always tell these docks
+         apart and the docks cannot report their true orientation on their own,
+         we rely on gravity to determine the effective orientation. -->
+    <bool name="config_deskDockEnablesAccelerometer">true</bool>
+
+    <!-- Car dock behavior -->
+
+    <!-- The number of degrees to rotate the display when the device is in a car dock.
+         A value of -1 means no change in orientation by default. -->
+    <integer name="config_carDockRotation">-1</integer>
+
+    <!-- Control whether being in the car dock (and powered) always
+         keeps the screen on.  By default it stays on when plugged in to
+         AC.  0 will not keep it on; or together 1 to stay on when plugged
+         in to AC and 2 to stay on when plugged in to USB.  (So 3 for both.) -->
+    <integer name="config_carDockKeepsScreenOn">1</integer>
+
+    <!-- Control whether being in the car dock should enable accelerometer based
+         screen orientation.  This defaults to true because putting a device in
+         a car dock make the accelerometer more a physical input (like a lid). -->
+
+    <bool name="config_carDockEnablesAccelerometer">true</bool>
+
+    <!-- HDMI behavior -->
+
+    <!-- The number of degrees to rotate the display when the device has HDMI connected
+         but is not in a dock.  A value of -1 means no change in orientation by default.
+         Use -1 except on older devices whose Hardware Composer HAL does not
+         provide full support for multiple displays.  -->
+    <integer name="config_undockedHdmiRotation">-1</integer>
+
+    <!-- Control the default UI mode type to use when there is no other type override
+         happening.  One of the following values (See Configuration.java):
+             1  UI_MODE_TYPE_NORMAL
+             4  UI_MODE_TYPE_TELEVISION
+             5  UI_MODE_TYPE_APPLIANCE
+         Any other values will have surprising consequences. -->
+    <integer name="config_defaultUiModeType">1</integer>
+
     <!-- Indicate whether to allow the device to suspend when the screen is off
          due to the proximity sensor.  This resource should only be set to true
          if the sensor HAL correctly handles the proximity sensor as a wake-up source.
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 8c8c322..8ec2e6f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -22,6 +22,10 @@
     <dimen name="thumbnail_width">164dp</dimen>
     <!-- The height that is used when creating thumbnails of applications. -->
     <dimen name="thumbnail_height">145dp</dimen>
+    <!-- The width that is used when creating thumbnails of applications. -->
+    <dimen name="recents_thumbnail_width">256dp</dimen>
+    <!-- The height that is used when creating thumbnails of applications. -->
+    <dimen name="recents_thumbnail_height">256dp</dimen>
     <!-- The standard size (both width and height) of an application icon that
          will be displayed in the app launcher and elsewhere. -->
     <dimen name="app_icon_size">48dip</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3106daa..d58e8ad 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2088,7 +2088,7 @@
   <public type="style" name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor" id="0x010301e4" />
 
 <!-- ===============================================================
-     Resources added in version 20 of the platform
+     Resources added in version 21 of the platform
      =============================================================== -->
   <eat-comment />
 
@@ -2098,7 +2098,6 @@
   <public type="attr" name="windowContentTransitionManager" />
   <public type="attr" name="translationZ" />
   <public type="attr" name="tintMode" />
-  <public type="attr" name="isolatedZVolume" />
   <public type="attr" name="controlX1" />
   <public type="attr" name="controlY1" />
   <public type="attr" name="controlX2" />
@@ -2107,6 +2106,10 @@
   <public type="attr" name="transitionGroup" />
   <public type="attr" name="castsShadow" />
   <public type="attr" name="requiredForProfile"/>
+  <public type="attr" name="pinned" />
+
+  <public type="dimen" name="recents_thumbnail_height" />
+  <public type="dimen" name="recents_thumbnail_width" />
 
   <public type="id" name="shared_element_name" />
 
@@ -2312,4 +2315,10 @@
   <public type="style" name="Quantum.Light.ButtonBar.AlertDialog" />
   <public type="style" name="Quantum.Light.ButtonBar" />
   <public type="style" name="Quantum.Light.SegmentedButton" />
+
+  <public type="style" name="Widget.Quantum.Button.Paper" />
+  <public type="style" name="Widget.Quantum.Button.Paper.Color" />
+
+  <public type="style" name="Widget.Quantum.Light.Button.Paper" />
+  <public type="style" name="Widget.Quantum.Light.Button.Paper.Color" />
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index afb7085..f1bcf65 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2012,6 +2012,10 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_accessNetworkConditions">Allows an application to listen for observations on network conditions. Should never be needed for normal apps.</string>
 
+    <string name="permlab_setInputCalibration">change input device calibration</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_setInputCalibration">Allows the app to modify the calibration parameters of the touch screen. Should never be needed for normal apps.</string>
+
     <!-- Policy administration -->
 
     <!-- Title of policy access to limiting the user's password choices -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3a45d8c..6624da4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1303,6 +1303,7 @@
   <java-symbol type="bool" name="config_lidControlsSleep" />
   <java-symbol type="bool" name="config_reverseDefaultRotation" />
   <java-symbol type="bool" name="config_showNavigationBar" />
+  <java-symbol type="bool" name="config_supportAutoRotation" />
   <java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
   <java-symbol type="dimen" name="navigation_bar_height" />
   <java-symbol type="dimen" name="navigation_bar_height_landscape" />
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
index 5a4a2d0..9d97ac5 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
@@ -101,9 +101,10 @@
     }
 
     /*
-     * Transition from CONNECTED -> DISCONNECTED:
-     *    CONNECTED->DISCONNECTING->DISCONNECTED
-     * return false if any state transition is not valid and save a message in mReason
+     * Verifies state transition from CONNECTED->...-> DISCONNECTED.
+     *
+     * returns false if initial state or target state is not correct, or if there is
+     * any transition from DISCONNECTING/DISCONNECTED -> CONNECTED.
      */
     public boolean transitToDisconnection () {
         mReason = "states: " + printStates();
@@ -120,13 +121,13 @@
         for (int i = 1; i < mStateDepository.size() - 1; i++) {
             State preState = mStateDepository.get(i-1);
             State curState = mStateDepository.get(i);
-            if ((preState == State.CONNECTED) && ((curState == State.DISCONNECTING) ||
+            if (preState == curState) {
+                continue;
+            } else if ((preState == State.CONNECTED) && ((curState == State.DISCONNECTING) ||
                     (curState == State.DISCONNECTED))) {
                 continue;
             } else if ((preState == State.DISCONNECTING) && (curState == State.DISCONNECTED)) {
                 continue;
-            } else if ((preState == State.DISCONNECTED) && (curState == State.DISCONNECTED)) {
-                continue;
             } else {
                 mReason += " Transition state from " + preState.toString() + " to " +
                         curState.toString() + " is not valid.";
@@ -136,7 +137,12 @@
         return true;
     }
 
-    // DISCONNECTED->CONNECTING->CONNECTED
+    /*
+     * Verifies state transition from DISCONNECTED->...-> CONNECTED.
+     *
+     * returns false if initial state or target state is not correct, or if there is
+     * any transition from CONNECED -> DISCONNECTED.
+     */
     public boolean transitToConnection() {
         mReason = "states: " + printStates();
         if (mStateDepository.get(0) != State.DISCONNECTED) {
@@ -152,14 +158,15 @@
         for (int i = 1; i < mStateDepository.size(); i++) {
             State preState = mStateDepository.get(i-1);
             State curState = mStateDepository.get(i);
+            if (preState == curState) {
+                continue;
+            }
             if ((preState == State.DISCONNECTED) && ((curState == State.CONNECTING) ||
-                    (curState == State.CONNECTED) || (curState == State.DISCONNECTED))) {
+                    (curState == State.CONNECTED))) {
                 continue;
-            } else if ((preState == State.CONNECTING) && (curState == State.CONNECTED)) {
-                continue;
-            } else if ((preState == State.CONNECTED) && (curState == State.CONNECTED)) {
-                continue;
-            } else {
+             } else if ((preState == State.CONNECTING) && (curState == State.CONNECTED)) {
+                 continue;
+             } else {
                 mReason += " Transition state from " + preState.toString() + " to " +
                         curState.toString() + " is not valid.";
                 return false;
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 2042345..0cdcb1c 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -161,8 +161,8 @@
 key 139   MENU              WAKE_DROPPED
 key 140   CALCULATOR
 # key 141 "KEY_SETUP"
-key 142   POWER             WAKE
-key 143   POWER             WAKE
+key 142   SLEEP             WAKE
+key 143   WAKEUP            WAKE
 # key 144 "KEY_FILE"
 # key 145 "KEY_SENDFILE"
 # key 146 "KEY_DELETEFILE"
diff --git a/docs/html/google/auth/api-client.jd b/docs/html/google/auth/api-client.jd
index fda3310..402a95f 100644
--- a/docs/html/google/auth/api-client.jd
+++ b/docs/html/google/auth/api-client.jd
@@ -112,7 +112,7 @@
 href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html"
 >{@code ConnectionCallbacks}</a> and <a
 href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.OnConnectionFailedListener.html"
->{@code onConnectionFailedListener}</a>. These interfaces receive callbacks in
+>{@code OnConnectionFailedListener}</a>. These interfaces receive callbacks in
 response to the asynchronous <a
 href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"
 >{@code connect()}</a> method when the connection to Google Play services
@@ -512,7 +512,7 @@
     new GetFileTask().execute(filename);
 }
 
-private class GetFileTask extends AsyncTask<String, Void, Void> {
+private class GetFileTask extends AsyncTask&lt;String, Void, Void> {
     protected void doInBackground(String filename) {
         Query query = new Query.Builder()
                 .addFilter(Filters.eq(SearchableField.TITLE, filename))
diff --git a/docs/html/guide/topics/ui/controls/button.jd b/docs/html/guide/topics/ui/controls/button.jd
index 02597c8..b52c3e9 100644
--- a/docs/html/guide/topics/ui/controls/button.jd
+++ b/docs/html/guide/topics/ui/controls/button.jd
@@ -113,7 +113,7 @@
 
 <h3 id="ClickListener">Using an OnClickListener</h3>
 
-<p>You can also declare the click event handler pragmatically rather than in an XML layout. This
+<p>You can also declare the click event handler programmatically rather than in an XML layout. This
 might be necessary if you instantiate the {@link android.widget.Button} at runtime or you need to
 declare the click behavior in a {@link android.app.Fragment} subclass.</p>
 
diff --git a/docs/html/guide/topics/ui/controls/togglebutton.jd b/docs/html/guide/topics/ui/controls/togglebutton.jd
index c57b510..09af516 100644
--- a/docs/html/guide/topics/ui/controls/togglebutton.jd
+++ b/docs/html/guide/topics/ui/controls/togglebutton.jd
@@ -99,7 +99,7 @@
 
 <h3 id="ClickListener">Using an OnCheckedChangeListener</h3>
 
-<p>You can also declare a click event handler pragmatically rather than in an XML layout. This
+<p>You can also declare a click event handler programmatically rather than in an XML layout. This
 might be necessary if you instantiate the {@link android.widget.ToggleButton} or {@link
 android.widget.Switch} at runtime or you need to
 declare the click behavior in a {@link android.app.Fragment} subclass.</p>
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
new file mode 100644
index 0000000..e6a755f
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -0,0 +1,317 @@
+/*
+ * 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 android.graphics.drawable;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.graphics.Xfermode;
+
+/**
+ * A Drawable that wraps another Drawable.
+ */
+class DrawableWrapper extends Drawable implements Drawable.Callback {
+    private WrapperState mWrapperState;
+
+    /** Local drawable backed by its own constant state. */
+    private Drawable mWrappedDrawable;
+
+    private boolean mMutated;
+
+    /** @hide */
+    @Override
+    public boolean isProjected() {
+        return mWrappedDrawable.isProjected();
+    }
+
+    @Override
+    public void setAutoMirrored(boolean mirrored) {
+        mWrappedDrawable.setAutoMirrored(mirrored);
+    }
+
+    @Override
+    public boolean isAutoMirrored() {
+        return mWrappedDrawable.isAutoMirrored();
+    }
+
+    @Override
+    public int getMinimumWidth() {
+        return mWrappedDrawable.getMinimumWidth();
+    }
+
+    @Override
+    public int getMinimumHeight() {
+        return mWrappedDrawable.getMinimumHeight();
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mWrappedDrawable.getIntrinsicWidth();
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mWrappedDrawable.getIntrinsicHeight();
+    }
+
+    @Override
+    public Drawable getCurrent() {
+        return mWrappedDrawable.getCurrent();
+    }
+
+    @Override
+    public void invalidateDrawable(Drawable who) {
+        final Callback callback = getCallback();
+        if (callback != null) {
+            callback.invalidateDrawable(this);
+        }
+    }
+
+    @Override
+    public void scheduleDrawable(Drawable who, Runnable what, long when) {
+        final Callback callback = getCallback();
+        if (callback != null) {
+            callback.scheduleDrawable(this, what, when);
+        }
+    }
+
+    @Override
+    public void unscheduleDrawable(Drawable who, Runnable what) {
+        final Callback callback = getCallback();
+        if (callback != null) {
+            callback.unscheduleDrawable(this, what);
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        mWrappedDrawable.draw(canvas);
+    }
+
+    @Override
+    public int getChangingConfigurations() {
+        return mWrappedDrawable.getChangingConfigurations();
+    }
+
+    @Override
+    public boolean getPadding(Rect padding) {
+        return mWrappedDrawable.getPadding(padding);
+    }
+
+    @Override
+    public Rect getDirtyBounds() {
+        return mWrappedDrawable.getDirtyBounds();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean supportsHotspots() {
+        return mWrappedDrawable.supportsHotspots();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void setHotspot(int id, float x, float y) {
+        mWrappedDrawable.setHotspot(id, x, y);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void removeHotspot(int id) {
+        mWrappedDrawable.removeHotspot(id);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void clearHotspots() {
+        mWrappedDrawable.clearHotspots();
+    }
+
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        // Must call through to super().
+        super.setVisible(visible, restart);
+        return mWrappedDrawable.setVisible(visible, restart);
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mWrappedDrawable.setAlpha(alpha);
+    }
+
+    @Override
+    public int getAlpha() {
+        return mWrappedDrawable.getAlpha();
+    }
+
+    /** {@hide} */
+    @Override
+    public void setLayoutDirection(int layoutDirection) {
+        mWrappedDrawable.setLayoutDirection(layoutDirection);
+    }
+
+    /** {@hide} */
+    @Override
+    public int getLayoutDirection() {
+        return mWrappedDrawable.getLayoutDirection();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+        mWrappedDrawable.setColorFilter(cf);
+    }
+
+    @Override
+    public ColorFilter getColorFilter() {
+        return mWrappedDrawable.getColorFilter();
+    }
+
+    @Override
+    public void setFilterBitmap(boolean filter) {
+        mWrappedDrawable.setFilterBitmap(filter);
+    }
+
+    @Override
+    public void setXfermode(Xfermode mode) {
+        mWrappedDrawable.setXfermode(mode);
+    }
+
+    @Override
+    public int getOpacity() {
+        return mWrappedDrawable.getOpacity();
+    }
+
+    @Override
+    public boolean isStateful() {
+        return mWrappedDrawable.isStateful();
+    }
+    
+    @Override
+    public final boolean setState(int[] stateSet) {
+        return super.setState(stateSet);
+    }
+
+    @Override
+    public final int[] getState() {
+        return super.getState();
+    }
+
+    @Override
+    protected boolean onStateChange(int[] state) {
+        // Don't override setState(), getState().
+        return mWrappedDrawable.setState(state);
+    }
+
+    @Override
+    protected boolean onLevelChange(int level) {
+        // Don't override setLevel(), getLevel().
+        return mWrappedDrawable.setLevel(level);
+    }
+    
+    @Override
+    public final void setBounds(int left, int top, int right, int bottom) {
+        super.setBounds(left, top, right, bottom);
+    }
+    
+    @Override
+    public final void setBounds(Rect bounds) {
+        super.setBounds(bounds);
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        // Don't override setBounds(), getBounds().
+        mWrappedDrawable.setBounds(bounds);
+    }
+
+    protected void setConstantState(WrapperState wrapperState, Resources res) {
+        mWrapperState = wrapperState;
+
+        // Load a new drawable from the constant state.
+        if (wrapperState == null || wrapperState.mWrappedConstantState == null) {
+            mWrappedDrawable = null;
+        } else if (res != null) {
+            mWrappedDrawable = wrapperState.mWrappedConstantState.newDrawable(res);
+        } else {
+            mWrappedDrawable = wrapperState.mWrappedConstantState.newDrawable();
+        }
+    }
+
+    @Override
+    public ConstantState getConstantState() {
+        return mWrapperState;
+    }
+
+    @Override
+    public Drawable mutate() {
+        if (!mMutated) {
+            mWrappedDrawable = mWrappedDrawable.mutate();
+            mMutated = true;
+        }
+        return this;
+    }
+
+    /**
+     * Sets the wrapped drawable and update the constant state.
+     *
+     * @param drawable
+     * @param res
+     */
+    protected final void setDrawable(Drawable drawable, Resources res) {
+        if (mWrappedDrawable != null) {
+            mWrappedDrawable.setCallback(null);
+        }
+
+        mWrappedDrawable = drawable;
+
+        if (drawable != null) {
+            drawable.setCallback(this);
+
+            mWrapperState.mWrappedConstantState = drawable.getConstantState();
+        } else {
+            mWrapperState.mWrappedConstantState = null;
+        }
+    }
+
+    protected final Drawable getDrawable() {
+        return mWrappedDrawable;
+    }
+
+    static abstract class WrapperState extends ConstantState {
+        ConstantState mWrappedConstantState;
+
+        WrapperState(WrapperState orig) {
+            if (orig != null) {
+                mWrappedConstantState = orig.mWrappedConstantState;
+            }
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return mWrappedConstantState.getChangingConfigurations();
+        }
+    }
+}
diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
index 6fbcb53..f4f545c 100644
--- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
+++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
@@ -21,15 +21,18 @@
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.SparseArray;
 
+import com.android.internal.R;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -37,23 +40,9 @@
 import java.util.ArrayList;
 
 /**
- * An extension of LayerDrawable that is intended to react to touch hotspots
- * and reveal the second layer atop the first.
- * <p>
- * It can be defined in an XML file with the <code>&lt;reveal&gt;</code> element.
- * Each Drawable in the transition is defined in a nested <code>&lt;item&gt;</code>.
- * For more information, see the guide to <a href="{@docRoot}
- * guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
- *
- * @attr ref android.R.styleable#LayerDrawableItem_left
- * @attr ref android.R.styleable#LayerDrawableItem_top
- * @attr ref android.R.styleable#LayerDrawableItem_right
- * @attr ref android.R.styleable#LayerDrawableItem_bottom
- * @attr ref android.R.styleable#LayerDrawableItem_drawable
- * @attr ref android.R.styleable#LayerDrawableItem_id
  * @hide
  */
-public class TouchFeedbackDrawable extends Drawable {
+public class TouchFeedbackDrawable extends DrawableWrapper {
     private final Rect mTempRect = new Rect();
     private final Rect mPaddingRect = new Rect();
 
@@ -77,32 +66,43 @@
     /** Paint used to control appearance of ripples. */
     private Paint mRipplePaint;
 
+    /** Paint used to control reveal layer masking. */
+    private Paint mMaskingPaint;
+
     /** Target density of the display into which ripples are drawn. */
     private float mDensity = 1.0f;
 
     /** Whether the animation runnable has been posted. */
     private boolean mAnimating;
 
-    TouchFeedbackDrawable() {
-        this(new TouchFeedbackState(null), null);
+    /** The drawable to use as the mask. */
+    private Drawable mMask;
+
+    /* package */TouchFeedbackDrawable() {
+        this(null, null);
     }
 
     TouchFeedbackDrawable(TouchFeedbackState state, Resources res) {
+        mState = new TouchFeedbackState(state);
+        
+        setConstantState(mState, res);
+
         if (res != null) {
             mDensity = res.getDisplayMetrics().density;
         }
-
-        mState = state;
     }
+    
+    private void setConstantState(TouchFeedbackState wrapperState, Resources res) {
+        super.setConstantState(wrapperState, res);
 
-    @Override
-    public void setColorFilter(ColorFilter cf) {
-        // Not supported.
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        // Not supported.
+        // Load a new mask drawable from the constant state.
+        if (wrapperState == null || wrapperState.mMaskState == null) {
+            mMask = null;
+        } else if (res != null) {
+            mMask = wrapperState.mMaskState.newDrawable(res);
+        } else {
+            mMask = wrapperState.mMaskState.newDrawable();
+        }
     }
 
     @Override
@@ -112,9 +112,20 @@
     }
 
     @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+
+        if (mMask != null) {
+            mMask.setBounds(bounds);
+        }
+    }
+
+    @Override
     protected boolean onStateChange(int[] stateSet) {
-        final ColorStateList stateList = mState.mColorStateList;
-        if (stateList != null && mRipplePaint != null) {
+        super.onStateChange(stateSet);
+
+        if (mRipplePaint != null) {
+            final ColorStateList stateList = mState.mTint;
             final int newColor = stateList.getColorForState(stateSet, 0);
             final int oldColor = mRipplePaint.getColor();
             if (oldColor != newColor) {
@@ -132,12 +143,12 @@
      */
     @Override
     public boolean isProjected() {
-        return true;
+        return mState.mProjected;
     }
 
     @Override
     public boolean isStateful() {
-        return mState.mColorStateList != null && mState.mColorStateList.isStateful();
+        return super.isStateful() || mState.mTint.isStateful();
     }
 
     @Override
@@ -145,16 +156,78 @@
             throws XmlPullParserException, IOException {
         super.inflate(r, parser, attrs);
 
-        final TypedArray a = r.obtainAttributes(
-                attrs, com.android.internal.R.styleable.ColorDrawable);
-        mState.mColorStateList = a.getColorStateList(
-                com.android.internal.R.styleable.ColorDrawable_color);
+        final TypedArray a = r.obtainAttributes(attrs, R.styleable.TouchFeedbackDrawable);
+
+        mState.mTint = a.getColorStateList(R.styleable.TouchFeedbackDrawable_tint);
+        mState.mTintMode = Drawable.parseTintMode(
+                a.getInt(R.styleable.TouchFeedbackDrawable_tintMode, -1), Mode.SRC_ATOP);
+        mState.mPinned = a.getBoolean(R.styleable.TouchFeedbackDrawable_pinned, false);
+
+        if (mState.mTint == null) {
+            throw new XmlPullParserException(parser.getPositionDescription()
+                    + ": <touch-feedback> tag requires a 'tint' attribute");
+        }
+        
+        Drawable mask = a.getDrawable(R.styleable.TouchFeedbackDrawable_mask);
+        final int drawableRes = a.getResourceId(R.styleable.TouchFeedbackDrawable_drawable, 0);
         a.recycle();
 
+        final Drawable dr;
+        if (drawableRes != 0) {
+            dr = r.getDrawable(drawableRes);
+        } else {
+            int type;
+            while ((type = parser.next()) == XmlPullParser.TEXT) {
+                // Find the next non-text element.
+            }
+
+            if (type == XmlPullParser.START_TAG) {
+                dr = Drawable.createFromXmlInner(r, parser, attrs);
+            } else {
+                dr = null;
+            }
+        }
+
+        // If no mask is set, implicitly use the lower drawable.
+        if (mask == null) {
+            mask = dr;
+        }
+
+        // If neither a mask not a bottom layer was specified, assume we're
+        // projecting onto a parent surface.
+        mState.mProjected = mask == null && dr == null;
+
+        if (dr != null) {
+            setDrawable(dr, r);
+        } else {
+            // For now at least, we MUST have a wrapped drawable.
+            setDrawable(new ColorDrawable(Color.TRANSPARENT), r);
+        }
+
+        setMaskDrawable(mask, r);
         setTargetDensity(r.getDisplayMetrics());
     }
 
     /**
+     * Sets the wrapped drawable and update the constant state.
+     *
+     * @param drawable
+     * @param res
+     */
+    void setMaskDrawable(Drawable drawable, Resources res) {
+        mMask = drawable;
+
+        if (drawable != null) {
+            // Nobody cares if the mask has a callback.
+            drawable.setCallback(null);
+
+            mState.mMaskState = drawable.getConstantState();
+        } else {
+            mState.mMaskState = null;
+        }
+    }
+
+    /**
      * Set the density at which this drawable will be rendered.
      *
      * @param metrics The display metrics for this drawable.
@@ -175,6 +248,9 @@
     }
 
     /**
+     * TODO: Maybe we should set hotspots for state/id combinations? So touch
+     * would be state_pressed and the pointer ID.
+     *
      * @hide until hotspot APIs are finalized
      */
     @Override
@@ -186,19 +262,22 @@
 
         final Ripple ripple = mTouchedRipples.get(id);
         if (ripple == null) {
+            final Rect bounds = getBounds();
             final Rect padding = mPaddingRect;
             getPadding(padding);
 
-            final Rect bounds = getBounds();
-            final Ripple newRipple = new Ripple(bounds, padding, bounds.exactCenterX(),
-                    bounds.exactCenterY(), mDensity);
+            if (mState.mPinned) {
+                x = bounds.exactCenterX();
+                y = bounds.exactCenterY();
+            }
+
+            final Ripple newRipple = new Ripple(bounds, padding, x, y, mDensity);
             newRipple.enter();
 
             mActiveRipples.add(newRipple);
             mTouchedRipples.put(id, newRipple);
-        } else {
-            // TODO: How do we want to respond to movement?
-            //ripple.move(x, y);
+        } else if (!mState.mPinned) {
+            ripple.move(x, y);
         }
 
         scheduleAnimation();
@@ -254,7 +333,7 @@
 
             if (mAnimationRunnable == null) {
                 mAnimationRunnable = new Runnable() {
-                    @Override
+                        @Override
                     public void run() {
                         mAnimating = false;
                         scheduleAnimation();
@@ -269,47 +348,77 @@
 
     @Override
     public void draw(Canvas canvas) {
+        // The lower layer always draws normally.
+        super.draw(canvas);
+
+        if (mActiveRipples == null || mActiveRipples.size() == 0) {
+            // No ripples to draw.
+            return;
+        }
+
         final ArrayList<Ripple> activeRipples = mActiveRipples;
-        if (activeRipples == null || activeRipples.isEmpty()) {
-            // Nothing to draw, we're done here.
-            return;
-        }
+        final Drawable mask = mMask;
+        final Rect bounds = mask == null ? null : mask.getBounds();
 
-        final ColorStateList stateList = mState.mColorStateList;
-        if (stateList == null) {
-            // No color, we're done here.
-            return;
-        }
-
-        final int color = stateList.getColorForState(getState(), Color.TRANSPARENT);
-        if (color == Color.TRANSPARENT) {
-            // No color, we're done here.
-            return;
-        }
-
-        if (mRipplePaint == null) {
-            mRipplePaint = new Paint();
-            mRipplePaint.setAntiAlias(true);
-        }
-
-        mRipplePaint.setColor(color);
-
-        final int restoreCount = canvas.save();
-
-        // Draw ripples directly onto the canvas.
+        // Draw ripples into a layer that merges using SRC_IN.
+        boolean hasRipples = false;
+        int rippleRestoreCount = -1;
         int n = activeRipples.size();
         for (int i = 0; i < n; i++) {
             final Ripple ripple = activeRipples.get(i);
             if (!ripple.active()) {
+                // TODO: Mark and sweep is more efficient.
                 activeRipples.remove(i);
                 i--;
                 n--;
             } else {
-                ripple.draw(canvas, mRipplePaint);
+                // If we're masking the ripple layer, make sure we have a layer first.
+                if (mask != null && rippleRestoreCount < 0) {
+                    rippleRestoreCount = canvas.saveLayer(bounds.left, bounds.top,
+                            bounds.right, bounds.bottom, getMaskingPaint(SRC_ATOP), 0);
+                    canvas.clipRect(bounds);
+                }
+
+                hasRipples |= ripple.draw(canvas, getRipplePaint());
             }
         }
 
-        canvas.restoreToCount(restoreCount);
+        // If we have ripples, mask them.
+        if (mask != null && hasRipples) {
+            canvas.saveLayer(bounds.left, bounds.top, bounds.right,
+                    bounds.bottom, getMaskingPaint(DST_IN), 0);
+            mask.draw(canvas);
+        }
+
+        // Composite the layers if needed:
+        // 1. Mask     DST_IN
+        // 2. Ripples  SRC_ATOP
+        // 3. Lower    n/a
+        if (rippleRestoreCount > 0) {
+            canvas.restoreToCount(rippleRestoreCount);
+        }
+    }
+
+    private Paint getRipplePaint() {
+        if (mRipplePaint == null) {
+            mRipplePaint = new Paint();
+            mRipplePaint.setAntiAlias(true);
+            
+            final int color = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
+            mRipplePaint.setColor(color);
+        }
+        return mRipplePaint;
+    }
+    
+    private static final PorterDuffXfermode SRC_ATOP = new PorterDuffXfermode(Mode.SRC_ATOP);
+    private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(Mode.DST_IN);
+
+    private Paint getMaskingPaint(PorterDuffXfermode mode) {
+        if (mMaskingPaint == null) {
+            mMaskingPaint = new Paint();
+        }
+        mMaskingPaint.setXfermode(mode);
+        return mMaskingPaint;
     }
 
     @Override
@@ -322,39 +431,46 @@
         final Rect rippleBounds = mTempRect;
         final ArrayList<Ripple> activeRipples = mActiveRipples;
         if (activeRipples != null) {
-           final int N = activeRipples.size();
-           for (int i = 0; i < N; i++) {
-               activeRipples.get(i).getBounds(rippleBounds);
-               drawingBounds.union(rippleBounds);
-           }
+            final int N = activeRipples.size();
+            for (int i = 0; i < N; i++) {
+                activeRipples.get(i).getBounds(rippleBounds);
+                drawingBounds.union(rippleBounds);
+            }
         }
 
         dirtyBounds.union(drawingBounds);
+        dirtyBounds.union(super.getDirtyBounds());
         return dirtyBounds;
     }
 
     @Override
     public ConstantState getConstantState() {
+        // TODO: Can we just rely on super.getConstantState()?
         return mState;
     }
 
-    static class TouchFeedbackState extends ConstantState {
-        ColorStateList mColorStateList;
+    static class TouchFeedbackState extends WrapperState {
+        ConstantState mMaskState;
+        ColorStateList mTint;
+        Mode mTintMode;
+        boolean mPinned;
+        boolean mProjected;
 
         public TouchFeedbackState(TouchFeedbackState orig) {
+            super(orig);
+
             if (orig != null) {
-                mColorStateList = orig.mColorStateList;
+                mTint = orig.mTint;
+                mTintMode = orig.mTintMode;
+                mMaskState = orig.mMaskState;
+                mPinned = orig.mPinned;
+                mProjected = orig.mProjected;
             }
         }
 
         @Override
-        public int getChangingConfigurations() {
-            return 0;
-        }
-
-        @Override
         public Drawable newDrawable() {
-            return newDrawable(null);
+            return new TouchFeedbackDrawable(this, null);
         }
 
         @Override
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index a0bae12..b334aab 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -79,7 +79,7 @@
  * two stretchable slices is exactly the ratio of their corresponding
  * segment lengths.
  *
- * xDivs and yDivs point to arrays of horizontal and vertical pixel
+ * xDivs and yDivs are arrays of horizontal and vertical pixel
  * indices.  The first pair of Divs (in either array) indicate the
  * starting and ending points of the first stretchable segment in that
  * axis. The next pair specifies the next stretchable segment, etc. So
@@ -92,32 +92,31 @@
  * go to xDiv[0] and slices 2, 6 and 10 start at xDiv[1] and end at
  * xDiv[2].
  *
- * The array pointed to by the colors field lists contains hints for
- * each of the regions.  They are ordered according left-to-right and
- * top-to-bottom as indicated above. For each segment that is a solid
- * color the array entry will contain that color value; otherwise it
- * will contain NO_COLOR.  Segments that are completely transparent
- * will always have the value TRANSPARENT_COLOR.
+ * The colors array contains hints for each of the regions. They are
+ * ordered according left-to-right and top-to-bottom as indicated above.
+ * For each segment that is a solid color the array entry will contain
+ * that color value; otherwise it will contain NO_COLOR. Segments that
+ * are completely transparent will always have the value TRANSPARENT_COLOR.
  *
  * The PNG chunk type is "npTc".
  */
 struct Res_png_9patch
 {
-    Res_png_9patch() : wasDeserialized(false), xDivs(NULL),
-                       yDivs(NULL), colors(NULL) { }
+    Res_png_9patch() : wasDeserialized(false), xDivsOffset(0),
+                       yDivsOffset(0), colorsOffset(0) { }
 
     int8_t wasDeserialized;
     int8_t numXDivs;
     int8_t numYDivs;
     int8_t numColors;
 
-    // These tell where the next section of a patch starts.
-    // For example, the first patch includes the pixels from
-    // 0 to xDivs[0]-1 and the second patch includes the pixels
-    // from xDivs[0] to xDivs[1]-1.
-    // Note: allocation/free of these pointers is left to the caller.
-    int32_t* xDivs;
-    int32_t* yDivs;
+    // The offset (from the start of this structure) to the xDivs & yDivs
+    // array for this 9patch. To get a pointer to this array, call
+    // getXDivs or getYDivs. Note that the serialized form for 9patches places
+    // the xDivs, yDivs and colors arrays immediately after the location
+    // of the Res_png_9patch struct.
+    uint32_t xDivsOffset;
+    uint32_t yDivsOffset;
 
     int32_t paddingLeft, paddingRight;
     int32_t paddingTop, paddingBottom;
@@ -129,22 +128,42 @@
         // The 9 patch segment is completely transparent.
         TRANSPARENT_COLOR = 0x00000000
     };
-    // Note: allocation/free of this pointer is left to the caller.
-    uint32_t* colors;
+
+    // The offset (from the start of this structure) to the colors array
+    // for this 9patch.
+    uint32_t colorsOffset;
 
     // Convert data from device representation to PNG file representation.
     void deviceToFile();
     // Convert data from PNG file representation to device representation.
     void fileToDevice();
-    // Serialize/Marshall the patch data into a newly malloc-ed block
-    void* serialize();
-    // Serialize/Marshall the patch data
-    void serialize(void* outData);
+
+    // Serialize/Marshall the patch data into a newly malloc-ed block.
+    static void* serialize(const Res_png_9patch& patchHeader, const int32_t* xDivs,
+                           const int32_t* yDivs, const uint32_t* colors);
+    // Serialize/Marshall the patch data into |outData|.
+    static void serialize(const Res_png_9patch& patchHeader, const int32_t* xDivs,
+                           const int32_t* yDivs, const uint32_t* colors, void* outData);
     // Deserialize/Unmarshall the patch data
-    static Res_png_9patch* deserialize(const void* data);
+    static Res_png_9patch* deserialize(void* data);
     // Compute the size of the serialized data structure
-    size_t serializedSize();
-};
+    size_t serializedSize() const;
+
+    // These tell where the next section of a patch starts.
+    // For example, the first patch includes the pixels from
+    // 0 to xDivs[0]-1 and the second patch includes the pixels
+    // from xDivs[0] to xDivs[1]-1.
+    inline int32_t* getXDivs() const {
+        return reinterpret_cast<int32_t*>(reinterpret_cast<uintptr_t>(this) + xDivsOffset);
+    }
+    inline int32_t* getYDivs() const {
+        return reinterpret_cast<int32_t*>(reinterpret_cast<uintptr_t>(this) + yDivsOffset);
+    }
+    inline uint32_t* getColors() const {
+        return reinterpret_cast<uint32_t*>(reinterpret_cast<uintptr_t>(this) + colorsOffset);
+    }
+
+} __attribute__((packed));
 
 /** ********************************************************************
  *  Base Types
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 51f59f6..98849e3 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -118,6 +118,12 @@
     return BAD_TYPE;
 }
 
+static void fill9patchOffsets(Res_png_9patch* patch) {
+    patch->xDivsOffset = sizeof(Res_png_9patch);
+    patch->yDivsOffset = patch->xDivsOffset + (patch->numXDivs * sizeof(int32_t));
+    patch->colorsOffset = patch->yDivsOffset + (patch->numYDivs * sizeof(int32_t));
+}
+
 inline void Res_value::copyFrom_dtoh(const Res_value& src)
 {
     size = dtohs(src.size);
@@ -128,9 +134,11 @@
 
 void Res_png_9patch::deviceToFile()
 {
+    int32_t* xDivs = getXDivs();
     for (int i = 0; i < numXDivs; i++) {
         xDivs[i] = htonl(xDivs[i]);
     }
+    int32_t* yDivs = getYDivs();
     for (int i = 0; i < numYDivs; i++) {
         yDivs[i] = htonl(yDivs[i]);
     }
@@ -138,6 +146,7 @@
     paddingRight = htonl(paddingRight);
     paddingTop = htonl(paddingTop);
     paddingBottom = htonl(paddingBottom);
+    uint32_t* colors = getColors();
     for (int i=0; i<numColors; i++) {
         colors[i] = htonl(colors[i]);
     }
@@ -145,9 +154,11 @@
 
 void Res_png_9patch::fileToDevice()
 {
+    int32_t* xDivs = getXDivs();
     for (int i = 0; i < numXDivs; i++) {
         xDivs[i] = ntohl(xDivs[i]);
     }
+    int32_t* yDivs = getYDivs();
     for (int i = 0; i < numYDivs; i++) {
         yDivs[i] = ntohl(yDivs[i]);
     }
@@ -155,60 +166,49 @@
     paddingRight = ntohl(paddingRight);
     paddingTop = ntohl(paddingTop);
     paddingBottom = ntohl(paddingBottom);
+    uint32_t* colors = getColors();
     for (int i=0; i<numColors; i++) {
         colors[i] = ntohl(colors[i]);
     }
 }
 
-size_t Res_png_9patch::serializedSize()
+size_t Res_png_9patch::serializedSize() const
 {
     // The size of this struct is 32 bytes on the 32-bit target system
     // 4 * int8_t
     // 4 * int32_t
-    // 3 * pointer
+    // 3 * uint32_t
     return 32
             + numXDivs * sizeof(int32_t)
             + numYDivs * sizeof(int32_t)
             + numColors * sizeof(uint32_t);
 }
 
-void* Res_png_9patch::serialize()
+void* Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs,
+                                const int32_t* yDivs, const uint32_t* colors)
 {
     // Use calloc since we're going to leave a few holes in the data
     // and want this to run cleanly under valgrind
-    void* newData = calloc(1, serializedSize());
-    serialize(newData);
+    void* newData = calloc(1, patch.serializedSize());
+    serialize(patch, xDivs, yDivs, colors, newData);
     return newData;
 }
 
-void Res_png_9patch::serialize(void * outData)
+void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs,
+                               const int32_t* yDivs, const uint32_t* colors, void* outData)
 {
-    char* data = (char*) outData;
-    memmove(data, &wasDeserialized, 4);     // copy  wasDeserialized, numXDivs, numYDivs, numColors
-    memmove(data + 12, &paddingLeft, 16);   // copy paddingXXXX
+    uint8_t* data = (uint8_t*) outData;
+    memcpy(data, &patch.wasDeserialized, 4);     // copy  wasDeserialized, numXDivs, numYDivs, numColors
+    memcpy(data + 12, &patch.paddingLeft, 16);   // copy paddingXXXX
     data += 32;
 
-    memmove(data, this->xDivs, numXDivs * sizeof(int32_t));
-    data +=  numXDivs * sizeof(int32_t);
-    memmove(data, this->yDivs, numYDivs * sizeof(int32_t));
-    data +=  numYDivs * sizeof(int32_t);
-    memmove(data, this->colors, numColors * sizeof(uint32_t));
-}
+    memcpy(data, xDivs, patch.numXDivs * sizeof(int32_t));
+    data +=  patch.numXDivs * sizeof(int32_t);
+    memcpy(data, yDivs, patch.numYDivs * sizeof(int32_t));
+    data +=  patch.numYDivs * sizeof(int32_t);
+    memcpy(data, colors, patch.numColors * sizeof(uint32_t));
 
-static void deserializeInternal(const void* inData, Res_png_9patch* outData) {
-    char* patch = (char*) inData;
-    if (inData != outData) {
-        memmove(&outData->wasDeserialized, patch, 4);     // copy  wasDeserialized, numXDivs, numYDivs, numColors
-        memmove(&outData->paddingLeft, patch + 12, 4);     // copy  wasDeserialized, numXDivs, numYDivs, numColors
-    }
-    outData->wasDeserialized = true;
-    char* data = (char*)outData;
-    data +=  sizeof(Res_png_9patch);
-    outData->xDivs = (int32_t*) data;
-    data +=  outData->numXDivs * sizeof(int32_t);
-    outData->yDivs = (int32_t*) data;
-    data +=  outData->numYDivs * sizeof(int32_t);
-    outData->colors = (uint32_t*) data;
+    fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData));
 }
 
 static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes)
@@ -312,14 +312,14 @@
     return NO_ERROR;
 }
 
-Res_png_9patch* Res_png_9patch::deserialize(const void* inData)
+Res_png_9patch* Res_png_9patch::deserialize(void* inData)
 {
-    if (sizeof(void*) != sizeof(int32_t)) {
-        ALOGE("Cannot deserialize on non 32-bit system\n");
-        return NULL;
-    }
-    deserializeInternal(inData, (Res_png_9patch*) inData);
-    return (Res_png_9patch*) inData;
+
+    Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(inData);
+    patch->wasDeserialized = true;
+    fill9patchOffsets(patch);
+
+    return patch;
 }
 
 // --------------------------------------------------------------------
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index 4935b34..8327ef7 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -47,9 +47,8 @@
         const Vector3& centroid3d, float heightFactor, float geomFactor,
         VertexBuffer& shadowVertexBuffer) {
     const int rays = SHADOW_RAY_COUNT;
-    const int layers = SHADOW_LAYER_COUNT;
     // Validate the inputs.
-    if (vertexCount < 3 || heightFactor <= 0 || layers <= 0 || rays <= 0
+    if (vertexCount < 3 || heightFactor <= 0 || rays <= 0
         || geomFactor <= 0) {
 #if DEBUG_SHADOW
         ALOGE("Invalid input for createAmbientShadow(), early return!");
@@ -96,33 +95,32 @@
     // calculate the normal N, which should be perpendicular to the edge of the
     // polygon (represented by the neighbor intersection points) .
     // Shadow's vertices will be generated as : P + N * scale.
-    int currentVertexIndex = 0;
-    for (int layerIndex = 0; layerIndex <= layers; layerIndex++) {
-        for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
+    for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
+        Vector2 normal(1.0f, 0.0f);
+        calculateNormal(rays, rayIndex, dir.array(), rayDist, normal);
 
-            Vector2 normal(1.0f, 0.0f);
-            calculateNormal(rays, rayIndex, dir.array(), rayDist, normal);
+        // The vertex should be start from rayDist[i] then scale the
+        // normalizeNormal!
+        Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] +
+                Vector2(centroid3d.x, centroid3d.y);
 
-            float opacity = 1.0 / (1 + rayHeight[rayIndex] / heightFactor);
+        // outer ring of points, expanded based upon height of each ray intersection
+        float expansionDist = rayHeight[rayIndex] * heightFactor *
+                geomFactor;
+        AlphaVertex::set(&shadowVertices[rayIndex],
+                intersection.x + normal.x * expansionDist,
+                intersection.y + normal.y * expansionDist,
+                0.0f);
 
-            // The vertex should be start from rayDist[i] then scale the
-            // normalizeNormal!
-            Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] +
-                    Vector2(centroid3d.x, centroid3d.y);
-
-            float layerRatio = layerIndex / (float)(layers);
-            // The higher the intersection is, the further the ambient shadow expanded.
-            float expansionDist = rayHeight[rayIndex] / heightFactor *
-                    geomFactor * (1 - layerRatio);
-            AlphaVertex::set(&shadowVertices[currentVertexIndex++],
-                    intersection.x + normal.x * expansionDist,
-                    intersection.y + normal.y * expansionDist,
-                    layerRatio * opacity);
-        }
-
+        // inner ring of points
+        float opacity = 1.0 / (1 + rayHeight[rayIndex] * heightFactor);
+        AlphaVertex::set(&shadowVertices[rayIndex + rays],
+                intersection.x,
+                intersection.y,
+                opacity);
     }
-    float centroidAlpha = 1.0 / (1 + centroid3d.z / heightFactor);
-    AlphaVertex::set(&shadowVertices[currentVertexIndex++],
+    float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor);
+    AlphaVertex::set(&shadowVertices[SHADOW_VERTEX_COUNT - 1],
             centroid3d.x, centroid3d.y, centroidAlpha);
 
 #if DEBUG_SHADOW
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 1d58d96..69d3328 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -702,11 +702,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void Caches::initTempProperties() {
-    propertyDirtyViewport = false;
-    propertyEnable3d = false;
-    propertyCameraDistance = 1.0f;
-    propertyAmbientShadowStrength = 0x3f;
-    propertySpotShadowStrength = 0x3f;
+    propertyAmbientShadowStrength = 25;
+    propertySpotShadowStrength = 25;
 
     propertyLightPosXScale = 0.5f;
     propertyLightPosYScale = 0.0f;
@@ -715,17 +712,7 @@
 
 void Caches::setTempProperty(const char* name, const char* value) {
     ALOGD("setting property %s to %s", name, value);
-    if (!strcmp(name, "enable3d")) {
-        propertyEnable3d = !strcmp(value, "true");
-        propertyDirtyViewport = true;
-        ALOGD("enable3d = %d", propertyEnable3d);
-        return;
-    } else if (!strcmp(name, "cameraDistance")) {
-        propertyCameraDistance = fmin(fmax(atof(value), 0.001), 10);
-        propertyDirtyViewport = true;
-        ALOGD("camera dist multiplier = %.2f", propertyCameraDistance);
-        return;
-    } else if (!strcmp(name, "ambientShadowStrength")) {
+    if (!strcmp(name, "ambientShadowStrength")) {
         propertyAmbientShadowStrength = atoi(value);
         ALOGD("ambient shadow strength = 0x%x out of 0xff", propertyAmbientShadowStrength);
         return;
@@ -735,17 +722,14 @@
         return;
     } else if (!strcmp(name, "lightPosXScale")) {
         propertyLightPosXScale = fmin(fmax(atof(value), 0.0), 1.0);
-        propertyDirtyViewport = true;
         ALOGD("lightPos X Scale = %.2f", propertyLightPosXScale);
         return;
     }  else if (!strcmp(name, "lightPosYScale")) {
         propertyLightPosYScale = fmin(fmax(atof(value), 0.0), 1.0);
-        propertyDirtyViewport = true;
         ALOGD("lightPos Y Scale = %.2f", propertyLightPosXScale);
         return;
     }  else if (!strcmp(name, "lightPosZScale")) {
         propertyLightPosZScale = fmin(fmax(atof(value), 0.0), 1.0);
-        propertyDirtyViewport = true;
         ALOGD("lightPos Z Scale = %.2f", propertyLightPosXScale);
         return;
     }
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 8c0c508..6f3d8fb 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -356,9 +356,6 @@
     // TEMPORARY properties
     void initTempProperties();
     void setTempProperty(const char* name, const char* value);
-    bool propertyEnable3d;
-    bool propertyDirtyViewport; // flag set when dirtying the viewport
-    float propertyCameraDistance;
 
     // These scaling factors range from 0 to 1, to scale the light position
     // within the bound of (screenwidth, screenheight, max(screenwidth, screenheight));
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 0f76486..b954c1f 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -160,8 +160,8 @@
                     mPivotY = mPrevHeight / 2.0f;
                 }
             }
-            const bool perspectiveEnabled = Caches::getInstance().propertyEnable3d;
-            if (!perspectiveEnabled && (mMatrixFlags & ROTATION_3D) == 0) {
+
+            if ((mMatrixFlags & ROTATION_3D) == 0) {
                 mTransformMatrix->loadTranslate(
                         mPivotX + mTranslationX,
                         mPivotY + mTranslationY,
@@ -170,38 +170,25 @@
                 mTransformMatrix->scale(mScaleX, mScaleY, 1);
                 mTransformMatrix->translate(-mPivotX, -mPivotY);
             } else {
-                if (perspectiveEnabled) {
-                    mTransformMatrix->loadTranslate(
-                            mPivotX + mTranslationX,
-                            mPivotY + mTranslationY,
-                            mTranslationZ);
-                    mTransformMatrix->rotate(mRotationX, 1, 0, 0);
-                    mTransformMatrix->rotate(mRotationY, 0, 1, 0);
-                    mTransformMatrix->rotate(mRotation, 0, 0, 1);
-                    mTransformMatrix->scale(mScaleX, mScaleY, 1);
-                    mTransformMatrix->translate(-mPivotX, -mPivotY);
-                } else {
-                    /* TODO: support this old transform approach, based on API level */
-                    if (!mTransformCamera) {
-                        mTransformCamera = new Sk3DView();
-                        mTransformMatrix3D = new SkMatrix();
-                    }
-                    SkMatrix transformMatrix;
-                    transformMatrix.reset();
-                    mTransformCamera->save();
-                    transformMatrix.preScale(mScaleX, mScaleY, mPivotX, mPivotY);
-                    mTransformCamera->rotateX(mRotationX);
-                    mTransformCamera->rotateY(mRotationY);
-                    mTransformCamera->rotateZ(-mRotation);
-                    mTransformCamera->getMatrix(mTransformMatrix3D);
-                    mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY);
-                    mTransformMatrix3D->postTranslate(mPivotX + mTranslationX,
-                            mPivotY + mTranslationY);
-                    transformMatrix.postConcat(*mTransformMatrix3D);
-                    mTransformCamera->restore();
-
-                    mTransformMatrix->load(transformMatrix);
+                if (!mTransformCamera) {
+                    mTransformCamera = new Sk3DView();
+                    mTransformMatrix3D = new SkMatrix();
                 }
+                SkMatrix transformMatrix;
+                transformMatrix.reset();
+                mTransformCamera->save();
+                transformMatrix.preScale(mScaleX, mScaleY, mPivotX, mPivotY);
+                mTransformCamera->rotateX(mRotationX);
+                mTransformCamera->rotateY(mRotationY);
+                mTransformCamera->rotateZ(-mRotation);
+                mTransformCamera->getMatrix(mTransformMatrix3D);
+                mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY);
+                mTransformMatrix3D->postTranslate(mPivotX + mTranslationX,
+                        mPivotY + mTranslationY);
+                transformMatrix.postConcat(*mTransformMatrix3D);
+                mTransformCamera->restore();
+
+                mTransformMatrix->load(transformMatrix);
             }
         }
         mMatrixDirty = false;
@@ -278,8 +265,7 @@
     }
     if (mMatrixFlags != 0) {
         if (mMatrixFlags == TRANSLATION) {
-            renderer.translate(mTranslationX, mTranslationY,
-                    Caches::getInstance().propertyEnable3d ? mTranslationZ : 0.0f); // TODO: necessary?
+            renderer.translate(mTranslationX, mTranslationY);
         } else {
             renderer.concatMatrix(*mTransformMatrix);
         }
@@ -318,8 +304,11 @@
 
 /**
  * Apply property-based transformations to input matrix
+ *
+ * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
+ * matrix computation instead of the Skia 3x3 matrix + camera hackery.
  */
-void DisplayList::applyViewPropertyTransforms(mat4& matrix) {
+void DisplayList::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
     if (mLeft != 0 || mTop != 0) {
         matrix.translate(mLeft, mTop);
     }
@@ -333,15 +322,31 @@
     if (mMatrixFlags != 0) {
         updateMatrix();
         if (mMatrixFlags == TRANSLATION) {
-            matrix.translate(mTranslationX, mTranslationY, mTranslationZ);
+            matrix.translate(mTranslationX, mTranslationY,
+                    true3dTransform ? mTranslationZ : 0.0f);
         } else {
-            matrix.multiply(*mTransformMatrix);
+            if (!true3dTransform) {
+                matrix.multiply(*mTransformMatrix);
+            } else {
+                mat4 true3dMat;
+                true3dMat.loadTranslate(
+                        mPivotX + mTranslationX,
+                        mPivotY + mTranslationY,
+                        mTranslationZ);
+                true3dMat.rotate(mRotationX, 1, 0, 0);
+                true3dMat.rotate(mRotationY, 0, 1, 0);
+                true3dMat.rotate(mRotation, 0, 0, 1);
+                true3dMat.scale(mScaleX, mScaleY, 1);
+                true3dMat.translate(-mPivotX, -mPivotY);
+
+                matrix.multiply(true3dMat);
+            }
         }
     }
 }
 
 /**
- * Organizes the DisplayList hierarchy to prepare for Z-based draw order.
+ * Organizes the DisplayList hierarchy to prepare for background projection reordering.
  *
  * This should be called before a call to defer() or drawDisplayList()
  *
@@ -350,7 +355,6 @@
  */
 void DisplayList::computeOrdering() {
     ATRACE_CALL();
-    m3dNodes.clear();
     mProjectedNodes.clear();
 
     // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
@@ -359,40 +363,23 @@
     for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
         DrawDisplayListOp* childOp = mDisplayListData->children[i];
         childOp->mDisplayList->computeOrderingImpl(childOp,
-                &m3dNodes, &mat4::identity(),
                 &mProjectedNodes, &mat4::identity());
     }
 }
 
 void DisplayList::computeOrderingImpl(
         DrawDisplayListOp* opState,
-        Vector<ZDrawDisplayListOpPair>* compositedChildrenOf3dRoot,
-        const mat4* transformFrom3dRoot,
         Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
         const mat4* transformFromProjectionSurface) {
-    m3dNodes.clear();
     mProjectedNodes.clear();
     if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
 
     // TODO: should avoid this calculation in most cases
     // TODO: just calculate single matrix, down to all leaf composited elements
-    Matrix4 localTransformFrom3dRoot(*transformFrom3dRoot);
-    localTransformFrom3dRoot.multiply(opState->mTransformFromParent);
     Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
     localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
 
-    if (mTranslationZ != 0.0f) { // TODO: other signals for 3d compositing, such as custom matrix4
-        // composited 3d layer, flag for out of order draw and save matrix...
-        opState->mSkipInOrderDraw = true;
-        opState->mTransformFromCompositingAncestor.load(localTransformFrom3dRoot);
-
-        // ... and insert into current 3d root, keyed with pivot z for later sorting
-        Vector3 pivot(mPivotX, mPivotY, 0.0f);
-        mat4 totalTransform(localTransformFrom3dRoot);
-        applyViewPropertyTransforms(totalTransform);
-        totalTransform.mapPoint3d(pivot);
-        compositedChildrenOf3dRoot->add(ZDrawDisplayListOpPair(pivot.z, opState));
-    } else if (mProjectBackwards) {
+    if (mProjectBackwards) {
         // composited projectee, flag for out of order draw, save matrix, and store in proj surface
         opState->mSkipInOrderDraw = true;
         opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
@@ -403,15 +390,6 @@
     }
 
     if (mDisplayListData->children.size() > 0) {
-        if (mIsolatedZVolume) {
-            // create a new 3d space for descendents by collecting them
-            compositedChildrenOf3dRoot = &m3dNodes;
-            transformFrom3dRoot = &mat4::identity();
-        } else {
-            applyViewPropertyTransforms(localTransformFrom3dRoot);
-            transformFrom3dRoot = &localTransformFrom3dRoot;
-        }
-
         const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
         bool haveAppliedPropertiesToProjection = false;
         for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
@@ -436,9 +414,7 @@
                 projectionChildren = compositedChildrenOfProjectionSurface;
                 projectionTransform = &localTransformFromProjectionSurface;
             }
-            child->computeOrderingImpl(childOp,
-                    compositedChildrenOf3dRoot, transformFrom3dRoot,
-                    projectionChildren, projectionTransform);
+            child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
         }
     }
 
@@ -491,14 +467,36 @@
             replayStruct.mDrawGlStatus);
 }
 
-#define SHADOW_DELTA 2.0f
+void DisplayList::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
+    if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return;
+
+    for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+        DrawDisplayListOp* childOp = mDisplayListData->children[i];
+        DisplayList* child = childOp->mDisplayList;
+        float childZ = child->mTranslationZ;
+
+        if (childZ != 0.0f) {
+            zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
+            childOp->mSkipInOrderDraw = true;
+        } else if (!child->mProjectBackwards) {
+            // regular, in order drawing DisplayList
+            childOp->mSkipInOrderDraw = false;
+        }
+    }
+
+    // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
+    std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
+}
+
+#define SHADOW_DELTA 0.1f
 
 template <class T>
-void DisplayList::iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer,
-        T& handler, const int level) {
-    if (m3dNodes.size() == 0 ||
-            (mode == kNegativeZChildren && m3dNodes[0].key > 0.0f) ||
-            (mode == kPositiveZChildren && m3dNodes[m3dNodes.size() - 1].key < 0.0f)) {
+void DisplayList::iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+        ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
+    const int size = zTranslatedNodes.size();
+    if (size == 0
+            || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
+            || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
         // no 3d children to draw
         return;
     }
@@ -516,7 +514,7 @@
      * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
      * underneath both, and neither's shadow is drawn on top of the other.
      */
-    const size_t nonNegativeIndex = findNonNegativeIndex(m3dNodes);
+    const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
     size_t drawIndex, shadowIndex, endIndex;
     if (mode == kNegativeZChildren) {
         drawIndex = 0;
@@ -524,24 +522,29 @@
         shadowIndex = endIndex; // draw no shadows
     } else {
         drawIndex = nonNegativeIndex;
-        endIndex = m3dNodes.size();
+        endIndex = size;
         shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
     }
     float lastCasterZ = 0.0f;
     while (shadowIndex < endIndex || drawIndex < endIndex) {
         if (shadowIndex < endIndex) {
-            DrawDisplayListOp* casterOp = m3dNodes[shadowIndex].value;
+            DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
             DisplayList* caster = casterOp->mDisplayList;
-            const float casterZ = m3dNodes[shadowIndex].key;
+            const float casterZ = zTranslatedNodes[shadowIndex].key;
             // attempt to render the shadow if the caster about to be drawn is its caster,
             // OR if its caster's Z value is similar to the previous potential caster
             if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
 
                 if (caster->mCastsShadow && caster->mAlpha > 0.0f) {
-                    mat4 shadowMatrix(casterOp->mTransformFromCompositingAncestor);
-                    caster->applyViewPropertyTransforms(shadowMatrix);
+                    mat4 shadowMatrixXY(casterOp->mTransformFromParent);
+                    caster->applyViewPropertyTransforms(shadowMatrixXY);
 
-                    DisplayListOp* shadowOp  = new (alloc) DrawShadowOp(shadowMatrix,
+                    // Z matrix needs actual 3d transformation, so mapped z values will be correct
+                    mat4 shadowMatrixZ(casterOp->mTransformFromParent);
+                    caster->applyViewPropertyTransforms(shadowMatrixZ, true);
+
+                    DisplayListOp* shadowOp  = new (alloc) DrawShadowOp(
+                            shadowMatrixXY, shadowMatrixZ,
                             caster->mAlpha, &(caster->mOutline), caster->mWidth, caster->mHeight);
                     handler(shadowOp, PROPERTY_SAVECOUNT, mClipToBounds);
                 }
@@ -556,10 +559,10 @@
         // since it modifies the renderer's matrix
         int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
 
-        DrawDisplayListOp* childOp = m3dNodes[drawIndex].value;
+        DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
         DisplayList* child = childOp->mDisplayList;
 
-        renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
+        renderer.concatMatrix(childOp->mTransformFromParent);
         childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
         handler(childOp, renderer.getSaveCount() - 1, mClipToBounds);
         childOp->mSkipInOrderDraw = true;
@@ -631,11 +634,11 @@
 
     bool quickRejected = mClipToBounds && renderer.quickRejectConservative(0, 0, mWidth, mHeight);
     if (!quickRejected) {
-        // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
-        std::stable_sort(m3dNodes.begin(), m3dNodes.end());
+        Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
+        buildZSortedChildList(zTranslatedNodes);
 
         // for 3d root, draw children with negative z values
-        iterate3dChildren(kNegativeZChildren, renderer, handler, level);
+        iterate3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
 
         DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
         const int saveCountOffset = renderer.getSaveCount() - 1;
@@ -656,7 +659,7 @@
         }
 
         // for 3d root, draw children with positive z values
-        iterate3dChildren(kPositiveZChildren, renderer, handler, level);
+        iterate3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
     }
 
     DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index a3577d4..189b544a 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -573,20 +573,20 @@
 
     void outputViewProperties(const int level);
 
-    void applyViewPropertyTransforms(mat4& matrix);
+    void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
 
     void computeOrderingImpl(DrawDisplayListOp* opState,
-            Vector<ZDrawDisplayListOpPair>* compositedChildrenOf3dRoot,
-            const mat4* transformFrom3dRoot,
             Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
             const mat4* transformFromProjectionSurface);
 
     template <class T>
     inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level);
 
+    void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes);
+
     template <class T>
-    inline void iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer,
-        T& handler, const int level);
+    inline void iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+            ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
 
     template <class T>
     inline void iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level);
@@ -657,9 +657,6 @@
      * Draw time state - these properties are only set and used during rendering
      */
 
-    // for 3d roots, contains a z sorted list of all children items
-    Vector<ZDrawDisplayListOpPair> m3dNodes;
-
     // for projection surfaces, contains a list of all children items
     Vector<DrawDisplayListOp*> mProjectedNodes;
 }; // class DisplayList
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 65eda29..6ce8317 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1534,10 +1534,10 @@
     const mat4 mTransformFromParent;
 
     /**
-     * Holds the transformation between the 3d root OR projection surface ViewGroup and this
-     * DisplayList drawing instance. Represents any translations / transformations done within the
-     * drawing of the compositing ancestor ViewGroup's draw, before the draw of the View represented
-     * by this DisplayList draw instance.
+     * Holds the transformation between the projection surface ViewGroup and this DisplayList
+     * drawing instance. Represents any translations / transformations done within the drawing of
+     * the compositing ancestor ViewGroup's draw, before the draw of the View represented by this
+     * DisplayList draw instance.
      *
      * Note: doesn't include any transformation recorded within the DisplayList and its properties.
      */
@@ -1550,19 +1550,20 @@
  */
 class DrawShadowOp : public DrawOp {
 public:
-    DrawShadowOp(const mat4& transform, float alpha, const SkPath* outline,
+    DrawShadowOp(const mat4& transformXY, const mat4& transformZ, float alpha, const SkPath* outline,
             float fallbackWidth, float fallbackHeight)
-            : DrawOp(NULL), mTransform(transform), mAlpha(alpha), mOutline(outline),
+            : DrawOp(NULL), mTransformXY(transformXY), mTransformZ(transformZ),
+            mAlpha(alpha), mOutline(outline),
             mFallbackWidth(fallbackWidth), mFallbackHeight(fallbackHeight) {}
 
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
-        if (!mOutline->isEmpty()) {
-            return renderer.drawShadow(mTransform, mAlpha, mOutline);
+        if (mOutline->isEmpty()) {
+            SkPath fakeOutline;
+            fakeOutline.addRect(0, 0, mFallbackWidth, mFallbackHeight);
+            return renderer.drawShadow(mTransformXY, mTransformZ, mAlpha, &fakeOutline);
         }
 
-        SkPath fakeOutline;
-        fakeOutline.addRect(0, 0, mFallbackWidth, mFallbackHeight);
-        return renderer.drawShadow(mTransform, mAlpha, &fakeOutline);
+        return renderer.drawShadow(mTransformXY, mTransformZ, mAlpha, mOutline);
     }
 
     virtual void output(int level, uint32_t logFlags) const {
@@ -1572,7 +1573,8 @@
     virtual const char* name() { return "DrawShadow"; }
 
 private:
-    const mat4 mTransform;
+    const mat4 mTransformXY;
+    const mat4 mTransformZ;
     const float mAlpha;
     const SkPath* mOutline;
     const float mFallbackWidth;
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 4f5cd26..f06106b 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -372,84 +372,6 @@
     mType = kTypeUnknown;
 }
 
-// translated from android.opengl.Matrix#frustumM()
-void Matrix4::loadFrustum(float left, float top, float right, float bottom, float near, float far) {
-    float r_width  = 1.0f / (right - left);
-    float r_height = 1.0f / (top - bottom);
-    float r_depth  = 1.0f / (near - far);
-    float x = 2.0f * (near * r_width);
-    float y = 2.0f * (near * r_height);
-    float A = (right + left) * r_width;
-    float B = (top + bottom) * r_height;
-    float C = (far + near) * r_depth;
-    float D = 2.0f * (far * near * r_depth);
-
-    memset(&data, 0, sizeof(data));
-    mType = kTypeUnknown;
-
-    data[kScaleX] = x;
-    data[kScaleY] = y;
-    data[8] = A;
-    data[9] = B;
-    data[kScaleZ] = C;
-    data[kTranslateZ] = D;
-    data[11] = -1.0f;
-}
-
-// translated from android.opengl.Matrix#setLookAtM()
-void Matrix4::loadLookAt(float eyeX, float eyeY, float eyeZ,
-        float centerX, float centerY, float centerZ,
-        float upX, float upY, float upZ) {
-    float fx = centerX - eyeX;
-    float fy = centerY - eyeY;
-    float fz = centerZ - eyeZ;
-
-    // Normalize f
-    float rlf = 1.0f / sqrt(fx*fx + fy*fy + fz*fz);
-    fx *= rlf;
-    fy *= rlf;
-    fz *= rlf;
-
-    // compute s = f x up (x means "cross product")
-    float sx = fy * upZ - fz * upY;
-    float sy = fz * upX - fx * upZ;
-    float sz = fx * upY - fy * upX;
-
-    // and normalize s
-    float rls = 1.0f / sqrt(sx*sx + sy*sy + sz*sz);
-    sx *= rls;
-    sy *= rls;
-    sz *= rls;
-
-    // compute u = s x f
-    float ux = sy * fz - sz * fy;
-    float uy = sz * fx - sx * fz;
-    float uz = sx * fy - sy * fx;
-
-    mType = kTypeUnknown;
-    data[0] = sx;
-    data[1] = ux;
-    data[2] = -fx;
-    data[3] = 0.0f;
-
-    data[4] = sy;
-    data[5] = uy;
-    data[6] = -fy;
-    data[7] = 0.0f;
-
-    data[8] = sz;
-    data[9] = uz;
-    data[10] = -fz;
-    data[11] = 0.0f;
-
-    data[12] = 0.0f;
-    data[13] = 0.0f;
-    data[14] = 0.0f;
-    data[15] = 1.0f;
-
-    translate(-eyeX, -eyeY, -eyeZ);
-}
-
 void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) {
     loadIdentity();
 
@@ -463,9 +385,14 @@
     mType = kTypeTranslate | kTypeScale | kTypeRectToRect;
 }
 
+float Matrix4::mapZ(const Vector3& orig) const {
+    // duplicates logic for mapPoint3d's z coordinate
+    return orig.x * data[2] + orig.y * data[6] + orig.z * data[kScaleZ] + data[kTranslateZ];
+}
+
 void Matrix4::mapPoint3d(Vector3& vec) const {
     //TODO: optimize simple case
-    Vector3 orig(vec);
+    const Vector3 orig(vec);
     vec.x = orig.x * data[kScaleX] + orig.y * data[kSkewX] + orig.z * data[8] + data[kTranslateX];
     vec.y = orig.x * data[kSkewY] + orig.y * data[kScaleY] + orig.z * data[9] + data[kTranslateY];
     vec.z = orig.x * data[2] + orig.y * data[6] + orig.z * data[kScaleZ] + data[kTranslateZ];
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 5cd79b1..26cb05f 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -129,10 +129,6 @@
     void loadRotate(float angle);
     void loadRotate(float angle, float x, float y, float z);
     void loadMultiply(const Matrix4& u, const Matrix4& v);
-    void loadFrustum(float left, float top, float right, float bottom, float near, float far);
-    void loadLookAt(float eyeX, float eyeY, float eyeZ,
-            float centerX, float centerY, float centerZ,
-            float upX, float upY, float upZ);
 
     void loadOrtho(float left, float right, float bottom, float top, float near, float far);
 
@@ -203,6 +199,7 @@
     void copyTo(float* v) const;
     void copyTo(SkMatrix& v) const;
 
+    float mapZ(const Vector3& orig) const;
     void mapPoint3d(Vector3& vec) const;
     void mapPoint(float& x, float& y) const; // 2d only
     void mapRect(Rect& r) const; // 2d only
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index b620b80..83de772 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -170,21 +170,7 @@
 }
 
 void OpenGLRenderer::initViewport(int width, int height) {
-    if (mCaches.propertyEnable3d) {
-        // TODO: make view proj app configurable
-        float dist = std::max(width, height) * 1.5;
-        dist *= mCaches.propertyCameraDistance;
-        Matrix4 projection;
-        projection.loadFrustum(-width / 2, -height / 2, width / 2, height / 2, dist, 0);
-        Matrix4 view;
-        view.loadLookAt(0, 0, dist,
-                0, 0, 0,
-                0, 1, 0);
-        mViewProjMatrix.loadMultiply(projection, view);
-        mViewProjMatrix.translate(-width/2, -height/2);
-    } else {
-        mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1);
-    }
+    mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1);
 
     initializeViewport(width, height);
 }
@@ -1929,13 +1915,6 @@
 status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty,
         int32_t replayFlags) {
     status_t status;
-
-    if (mCaches.propertyDirtyViewport) {
-        // force recalc of view/proj matrices
-        setViewport(getWidth(), getHeight());
-        mCaches.propertyDirtyViewport = false;
-    }
-
     // All the usual checks and setup operations (quickReject, setupDraw, etc.)
     // will be performed by the display list itself
     if (displayList && displayList->isRenderable()) {
@@ -3211,8 +3190,16 @@
     return drawColorRects(rects, count, paint, false, true, true);
 }
 
-status_t OpenGLRenderer::drawShadow(const mat4& casterTransform, float casterAlpha,
-        const SkPath* casterOutline) {
+static void mapPointFakeZ(Vector3& point, const mat4& transformXY, const mat4& transformZ) {
+    // map z coordinate with true 3d matrix
+    point.z = transformZ.mapZ(point);
+
+    // map x,y coordinates with draw/Skia matrix
+    transformXY.mapPoint(point.x, point.y);
+}
+
+status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
+        float casterAlpha, const SkPath* casterOutline) {
     if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
 
     // TODO: use quickRejectWithScissor. For now, always force enable scissor.
@@ -3238,7 +3225,7 @@
     for (int i = 0; i < casterVertexCount; i++) {
         const Vertex& point2d = casterVertices2d[i];
         casterPolygon[i] = Vector3(point2d.x, point2d.y, 0);
-        casterTransform.mapPoint3d(casterPolygon[i]);
+        mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
     }
 
     // map the centroid of the caster into 3d
@@ -3246,7 +3233,7 @@
             reinterpret_cast<const Vector2*>(casterVertices2d.array()),
             casterVertexCount);
     Vector3 centroid3d(centroid.x, centroid.y, 0);
-    casterTransform.mapPoint3d(centroid3d);
+    mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
 
     // draw caster's shadows
     if (mCaches.propertyAmbientShadowStrength > 0) {
@@ -3265,7 +3252,6 @@
         ShadowTessellator::tessellateSpotShadow(casterPolygon, casterVertexCount,
                 lightPosScale, *currentTransform(), getWidth(), getHeight(),
                 spotShadowVertexBuffer);
-
         drawVertexBuffer(kVertexBufferMode_Shadow, spotShadowVertexBuffer, &paint);
     }
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 03beae3..76dd014 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -208,8 +208,8 @@
             DrawOpMode drawOpMode = kDrawOpMode_Immediate);
     virtual status_t drawRects(const float* rects, int count, const SkPaint* paint);
 
-    status_t drawShadow(const mat4& casterTransform, float casterAlpha,
-            const SkPath* casterOutline);
+    status_t drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
+            float casterAlpha, const SkPath* casterOutline);
 
     virtual void resetShader();
     virtual void setupShader(SkiaShader* shader);
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 9b023f9..b2148b0 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -57,7 +57,7 @@
     if (vertices) return vertices;
 
     int8_t emptyQuads = 0;
-    mColors = patch->colors;
+    mColors = patch->getColors();
 
     const int8_t numColors = patch->numColors;
     if (uint8_t(numColors) < sizeof(uint32_t) * 4) {
@@ -79,8 +79,8 @@
     TextureVertex* tempVertices = new TextureVertex[maxVertices];
     TextureVertex* vertex = tempVertices;
 
-    const int32_t* xDivs = patch->xDivs;
-    const int32_t* yDivs = patch->yDivs;
+    const int32_t* xDivs = patch->getXDivs();
+    const int32_t* yDivs = patch->getYDivs();
 
     const uint32_t xStretchCount = (xCount + 1) >> 1;
     const uint32_t yStretchCount = (yCount + 1) >> 1;
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index 489064b..1ba045d 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -67,7 +67,7 @@
     void generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2,
             float u1, float v1, float u2, float v2, uint32_t& quadCount);
 
-    uint32_t* mColors;
+    const uint32_t* mColors;
     UvMapper mUvMapper;
 }; // struct Patch
 
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index cc56333..4ef2158 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "OpenGLRenderer"
 #define LOG_NDEBUG 1
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#define ATRACE_TAG ATRACE_TAG_VIEW
 
 #define VERTEX_DEBUG 0
 
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 526772b..f138222 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -15,9 +15,11 @@
  */
 
 #define LOG_TAG "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
 
 #include <math.h>
 #include <utils/Log.h>
+#include <utils/Trace.h>
 
 #include "AmbientShadow.h"
 #include "ShadowTessellator.h"
@@ -34,9 +36,11 @@
 void ShadowTessellator::tessellateAmbientShadow(const Vector3* casterPolygon,
         int casterVertexCount, const Vector3& centroid3d,
         VertexBuffer& shadowVertexBuffer) {
+    ATRACE_CALL();
+
     // A bunch of parameters to tweak the shadow.
     // TODO: Allow some of these changable by debug settings or APIs.
-    const float heightFactor = 128;
+    const float heightFactor = 1.0f / 128;
     const float geomFactor = 64;
 
     AmbientShadow::createAmbientShadow(casterPolygon, casterVertexCount,
@@ -47,6 +51,8 @@
 void ShadowTessellator::tessellateSpotShadow(const Vector3* casterPolygon, int casterVertexCount,
         const Vector3& lightPosScale, const mat4& receiverTransform,
         int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer) {
+    ATRACE_CALL();
+
     // A bunch of parameters to tweak the shadow.
     // TODO: Allow some of these changable by debug settings or APIs.
     int maximal = max(screenWidth, screenHeight);
@@ -63,7 +69,7 @@
     reverseReceiverTransform.mapPoint3d(lightCenter);
 
     const float lightSize = maximal / 4;
-    const int lightVertexCount = 16;
+    const int lightVertexCount = 8;
 
     SpotShadow::createSpotShadow(casterPolygon, casterVertexCount, lightCenter,
             lightSize, lightVertexCount, shadowVertexBuffer);
@@ -72,26 +78,23 @@
 
 void ShadowTessellator::generateShadowIndices(uint16_t* shadowIndices) {
     int currentIndex = 0;
-    const int layers = SHADOW_LAYER_COUNT;
     const int rays = SHADOW_RAY_COUNT;
     // For the penumbra area.
-    for (int i = 0; i < layers; i++) {
-        for (int j = 0; j < rays; j++) {
-            shadowIndices[currentIndex++] = i * rays + j;
-            shadowIndices[currentIndex++] = (i + 1) * rays + j;
-        }
-        // To close the loop, back to the ray 0.
-        shadowIndices[currentIndex++] = i * rays;
-        shadowIndices[currentIndex++] = (i + 1) * rays;
+    for (int i = 0; i < rays; i++) {
+        shadowIndices[currentIndex++] = i;
+        shadowIndices[currentIndex++] = rays + i;
     }
-    uint16_t base = layers * rays;
-    uint16_t centroidIndex = (layers + 1) * rays;
+    // To close the loop, back to the ray 0.
+    shadowIndices[currentIndex++] = 0;
+    shadowIndices[currentIndex++] = rays;
+
+    uint16_t centroidIndex = 2 * rays;
     // For the umbra area, using strips to simulate the fans.
-    for (int k = 0; k < rays; k++) {
-        shadowIndices[currentIndex++] = base + k;
+    for (int i = 0; i < rays; i++) {
+        shadowIndices[currentIndex++] = rays + i;
         shadowIndices[currentIndex++] = centroidIndex;
     }
-    shadowIndices[currentIndex++] = base;
+    shadowIndices[currentIndex++] = rays;
 
 #if DEBUG_SHADOW
     if (currentIndex != SHADOW_INDEX_COUNT) {
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index c49fdcb..120774b 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -46,17 +46,13 @@
 
 // The total number of rays starting from the centroid of shadow area, in order
 // to generate the shadow geometry.
-#define SHADOW_RAY_COUNT 256
-
-// The total number of layers in the outer shadow area, 1 being the minimum.
-#define SHADOW_LAYER_COUNT 2
+#define SHADOW_RAY_COUNT 128
 
 // The total number of all the vertices representing the shadow.
-#define SHADOW_VERTEX_COUNT ((SHADOW_LAYER_COUNT + 1) * SHADOW_RAY_COUNT + 1)
+#define SHADOW_VERTEX_COUNT (2 * SHADOW_RAY_COUNT + 1)
 
 // The total number of indices used for drawing the shadow geometry as triangle strips.
-#define SHADOW_INDEX_COUNT (2 * SHADOW_RAY_COUNT + 1 + 2 * (SHADOW_RAY_COUNT + 1) * \
-        SHADOW_LAYER_COUNT)
+#define SHADOW_INDEX_COUNT (2 * SHADOW_RAY_COUNT + 1 + 2 * (SHADOW_RAY_COUNT + 1))
 
 class ShadowTessellator {
 public:
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index 22d735b..4e52555 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -29,45 +29,46 @@
 namespace android {
 namespace uirenderer {
 
+static const double EPSILON = 1e-7;
+
 /**
- * Calculate the intersection of a ray with a polygon.
- * It assumes the ray originates inside the polygon.
- *
- * @param poly The polygon, which is represented in a Vector2 array.
- * @param polyLength The length of caster's polygon in terms of number of
- *                   vertices.
- * @param point the start of the ray
- * @param dx the x vector of the ray
- * @param dy the y vector of the ray
- * @return the distance along the ray if it intersects with the polygon FP_NAN if otherwise
+ * Calculate the angle between and x and a y coordinate.
+ * The atan2 range from -PI to PI.
  */
-float SpotShadow::rayIntersectPoly(const Vector2* poly, int polyLength,
-        const Vector2& point, float dx, float dy) {
-    double px = point.x;
-    double py = point.y;
-    int p1 = polyLength - 1;
-    for (int p2 = 0; p2 < polyLength; p2++) {
-        double p1x = poly[p1].x;
-        double p1y = poly[p1].y;
-        double p2x = poly[p2].x;
-        double p2y = poly[p2].y;
-        // The math below is derived from solving this formula, basically the
-        // intersection point should stay on both the ray and the edge of (p1, p2).
-        // solve([p1x+t*(p2x-p1x)=dx*t2+px,p1y+t*(p2y-p1y)=dy*t2+py],[t,t2]);
-        double div = (dx * (p1y - p2y) + dy * p2x - dy * p1x);
-        if (div != 0) {
-            double t = (dx * (p1y - py) + dy * px - dy * p1x) / (div);
-            if (t >= 0 && t <= 1) {
-                double t2 = (p1x * (py - p2y) + p2x * (p1y - py) +
-                        px * (p2y - p1y)) / div;
-                if (t2 > 0) {
-                    return (float)t2;
-                }
-            }
-        }
-        p1 = p2;
-    }
-    return FP_NAN;
+static float angle(const Vector2& point, const Vector2& center) {
+    return atan2(point.y - center.y, point.x - center.x);
+}
+
+/**
+ * Calculate the intersection of a ray with the line segment defined by two points.
+ *
+ * Returns a negative value in error conditions.
+
+ * @param rayOrigin The start of the ray
+ * @param dx The x vector of the ray
+ * @param dy The y vector of the ray
+ * @param p1 The first point defining the line segment
+ * @param p2 The second point defining the line segment
+ * @return The distance along the ray if it intersects with the line segment, negative if otherwise
+ */
+static float rayIntersectPoints(const Vector2& rayOrigin, float dx, float dy,
+        const Vector2& p1, const Vector2& p2) {
+    // The math below is derived from solving this formula, basically the
+    // intersection point should stay on both the ray and the edge of (p1, p2).
+    // solve([p1x+t*(p2x-p1x)=dx*t2+px,p1y+t*(p2y-p1y)=dy*t2+py],[t,t2]);
+
+    double divisor = (dx * (p1.y - p2.y) + dy * p2.x - dy * p1.x);
+    if (divisor == 0) return -1.0f; // error, invalid divisor
+
+#if DEBUG_SHADOW
+    double interpVal = (dx * (p1.y - rayOrigin.y) + dy * rayOrigin.x - dy * p1.x) / divisor;
+    if (interpVal < 0 || interpVal > 1) return -1.0f; // error, doesn't intersect between points
+#endif
+
+    double distance = (p1.x * (rayOrigin.y - p2.y) + p2.x * (p1.y - rayOrigin.y) +
+            rayOrigin.x * (p2.y - p1.y)) / divisor;
+
+    return distance; // may be negative in error cases
 }
 
 /**
@@ -131,30 +132,26 @@
             lLowerSize--;
         }
     }
-    int count = 0;
 
+    // output points in CW ordering
+    const int total = lUpperSize + lLowerSize - 2;
+    int outIndex = total - 1;
     for (int i = 0; i < lUpperSize; i++) {
-        retPoly[count] = lUpper[i];
-        count++;
+        retPoly[outIndex] = lUpper[i];
+        outIndex--;
     }
 
     for (int i = 1; i < lLowerSize - 1; i++) {
-        retPoly[count] = lLower[i];
-        count++;
+        retPoly[outIndex] = lLower[i];
+        outIndex--;
     }
     // TODO: Add test harness which verify that all the points are inside the hull.
-    return count;
+    return total;
 }
 
 /**
  * Test whether the 3 points form a counter clockwise turn.
  *
- * @param ax the x coordinate of point a
- * @param ay the y coordinate of point a
- * @param bx the x coordinate of point b
- * @param by the y coordinate of point b
- * @param cx the x coordinate of point c
- * @param cy the y coordinate of point c
  * @return true if a right hand turn
  */
 bool SpotShadow::ccw(double ax, double ay, double bx, double by,
@@ -303,15 +300,6 @@
 }
 
 /**
- * Calculate the angle between and x and a y coordinate.
- * The atan2 range from -PI to PI, if we want to sort the vertices as clockwise,
- * we just negate the return angle.
- */
-float SpotShadow::angle(const Vector2& point, const Vector2& center) {
-    return -(float)atan2(point.y - center.y, point.x - center.x);
-}
-
-/**
  * Swap points pointed to by i and j
  */
 void SpotShadow::swap(Vector2* points, int i, int j) {
@@ -329,10 +317,10 @@
     int p = low + (high - low) / 2;
     float pivot = angle(points[p], center);
     while (i <= j) {
-        while (angle(points[i], center) < pivot) {
+        while (angle(points[i], center) > pivot) {
             i++;
         }
-        while (angle(points[j], center) > pivot) {
+        while (angle(points[j], center) < pivot) {
             j--;
         }
 
@@ -508,8 +496,8 @@
     // TODO: Caching all the sin / cos values and store them in a look up table.
     for (int i = 0; i < points; i++) {
         double angle = 2 * i * M_PI / points;
-        ret[i].x = sinf(angle) * size + lightCenter.x;
-        ret[i].y = cosf(angle) * size + lightCenter.y;
+        ret[i].x = cosf(angle) * size + lightCenter.x;
+        ret[i].y = sinf(angle) * size + lightCenter.y;
         ret[i].z = lightCenter.z;
     }
 }
@@ -562,12 +550,12 @@
     for (int i = 0; i < polyLength; i++) {
         if (poly[i].z <= 0.00001) {
             inputPolyPositionValid = false;
-            ALOGE("polygon below the surface");
+            ALOGW("polygon below the surface");
             break;
         }
         if (poly[i].z >= lightPoly[0].z) {
             inputPolyPositionValid = false;
-            ALOGE("polygon above the light");
+            ALOGW("polygon above the light");
             break;
         }
     }
@@ -657,6 +645,63 @@
 }
 
 /**
+ * Converts a polygon specified with CW vertices into an array of distance-from-centroid values.
+ *
+ * Returns false in error conditions
+ *
+ * @param poly Array of vertices. Note that these *must* be CW.
+ * @param polyLength The number of vertices in the polygon.
+ * @param polyCentroid The centroid of the polygon, from which rays will be cast
+ * @param rayDist The output array for the calculated distances, must be SHADOW_RAY_COUNT in size
+ */
+bool convertPolyToRayDist(const Vector2* poly, int polyLength, const Vector2& polyCentroid,
+        float* rayDist) {
+    const int rays = SHADOW_RAY_COUNT;
+    const float step = M_PI * 2 / rays;
+
+    const Vector2* lastVertex = &(poly[polyLength - 1]);
+    float startAngle = angle(*lastVertex, polyCentroid);
+
+    // Start with the ray that's closest to and less than startAngle
+    int rayIndex = floor((startAngle - EPSILON) / step);
+    rayIndex = (rayIndex + rays) % rays; // ensure positive
+
+    for (int polyIndex = 0; polyIndex < polyLength; polyIndex++) {
+        /*
+         * For a given pair of vertices on the polygon, poly[i-1] and poly[i], the rays that
+         * intersect these will be those that are between the two angles from the centroid that the
+         * vertices define.
+         *
+         * Because the polygon vertices are stored clockwise, the closest ray with an angle
+         * *smaller* than that defined by angle(poly[i], centroid) will be the first ray that does
+         * not intersect with poly[i-1], poly[i].
+         */
+        float currentAngle = angle(poly[polyIndex], polyCentroid);
+
+        // find first ray that will not intersect the line segment poly[i-1] & poly[i]
+        int firstRayIndexOnNextSegment = floor((currentAngle - EPSILON) / step);
+        firstRayIndexOnNextSegment = (firstRayIndexOnNextSegment + rays) % rays; // ensure positive
+
+        // Iterate through all rays that intersect with poly[i-1], poly[i] line segment.
+        // This may be 0 rays.
+        while (rayIndex != firstRayIndexOnNextSegment) {
+            float distanceToIntersect = rayIntersectPoints(polyCentroid,
+                    cos(rayIndex * step),
+                    sin(rayIndex * step),
+                    *lastVertex, poly[polyIndex]);
+            if (distanceToIntersect < 0) return false; // error case, abort
+
+            rayDist[rayIndex] = distanceToIntersect;
+
+            rayIndex = (rayIndex - 1 + rays) % rays;
+        }
+        lastVertex = &poly[polyIndex];
+    }
+
+   return true;
+}
+
+/**
  * Generate a triangle strip given two convex polygons
  *
  * @param penumbra The outer polygon x,y vertexes
@@ -669,10 +714,9 @@
 void SpotShadow::generateTriangleStrip(const Vector2* penumbra, int penumbraLength,
         const Vector2* umbra, int umbraLength, VertexBuffer& shadowTriangleStrip) {
     const int rays = SHADOW_RAY_COUNT;
-    const int layers = SHADOW_LAYER_COUNT;
 
-    int size = rays * (layers + 1);
-    float step = M_PI * 2 / rays;
+    const int size = 2 * rays;
+    const float step = M_PI * 2 / rays;
     // Centroid of the umbra.
     Vector2 centroid = ShadowTessellator::centroid2d(umbra, umbraLength);
 #if DEBUG_SHADOW
@@ -683,48 +727,31 @@
     // Intersection to the umbra.
     float umbraDistPerRay[rays];
 
-    for (int i = 0; i < rays; i++) {
-        // TODO: Setup a lookup table for all the sin/cos.
-        float dx = sinf(step * i);
-        float dy = cosf(step * i);
-        umbraDistPerRay[i] = rayIntersectPoly(umbra, umbraLength, centroid,
-                dx, dy);
-        if (isnan(umbraDistPerRay[i])) {
-            ALOGE("rayIntersectPoly returns NAN");
-            return;
-        }
-        penumbraDistPerRay[i] = rayIntersectPoly(penumbra, penumbraLength,
-                centroid, dx, dy);
-        if (isnan(umbraDistPerRay[i])) {
-            ALOGE("rayIntersectPoly returns NAN");
-            return;
-        }
-    }
+    // convert CW polygons to ray distance encoding, aborting on conversion failure
+    if (!convertPolyToRayDist(umbra, umbraLength, centroid, umbraDistPerRay)) return;
+    if (!convertPolyToRayDist(penumbra, penumbraLength, centroid, penumbraDistPerRay)) return;
 
-    int stripSize = getStripSize(rays, layers);
-    AlphaVertex* shadowVertices = shadowTriangleStrip.alloc<AlphaVertex>(stripSize);
-    int currentIndex = 0;
+    AlphaVertex* shadowVertices = shadowTriangleStrip.alloc<AlphaVertex>(getStripSize(rays));
 
     // Calculate the vertices (x, y, alpha) in the shadow area.
-    for (int layerIndex = 0; layerIndex <= layers; layerIndex++) {
-        for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
-            float dx = sinf(step * rayIndex);
-            float dy = cosf(step * rayIndex);
-                float layerRatio = layerIndex / (float) layers;
-                float deltaDist = layerRatio *
-                        (umbraDistPerRay[rayIndex] - penumbraDistPerRay[rayIndex]);
-                float currentDist = penumbraDistPerRay[rayIndex] + deltaDist;
-                float op = calculateOpacity(layerRatio);
-                AlphaVertex::set(&shadowVertices[currentIndex++],
-                        dx * currentDist + centroid.x, dy * currentDist + centroid.y, op);
-        }
+    for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
+        float dx = cosf(step * rayIndex);
+        float dy = sinf(step * rayIndex);
+
+        // outer ring
+        float currentDist = penumbraDistPerRay[rayIndex];
+        AlphaVertex::set(&shadowVertices[rayIndex],
+                dx * currentDist + centroid.x, dy * currentDist + centroid.y, 0.0f);
+
+        // inner ring
+        float deltaDist = umbraDistPerRay[rayIndex] - penumbraDistPerRay[rayIndex];
+        currentDist += deltaDist;
+        AlphaVertex::set(&shadowVertices[rays + rayIndex],
+                dx * currentDist + centroid.x, dy * currentDist + centroid.y, 1.0f);
     }
     // The centroid is in the umbra area, so the opacity is considered as 1.0.
-    AlphaVertex::set(&shadowVertices[currentIndex++], centroid.x, centroid.y, 1.0);
+    AlphaVertex::set(&shadowVertices[SHADOW_VERTEX_COUNT - 1], centroid.x, centroid.y, 1.0f);
 #if DEBUG_SHADOW
-    if (currentIndex != SHADOW_VERTEX_COUNT) {
-        ALOGE("number of vertex generated for spot shadow is wrong!");
-    }
     for (int i = 0; i < currentIndex; i++) {
         ALOGD("spot shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x,
                 shadowVertices[i].y, shadowVertices[i].alpha);
@@ -754,26 +781,14 @@
 }
 
 /**
- * Calculate the opacity according to the distance. Ideally, the opacity is 1.0
- * in the umbra area, and fall off to 0.0 till the edge of penumbra area.
- *
- * @param layerRatio The distance ratio of current sample between umbra and penumbra area.
- *                   Penumbra edge is 0 and umbra edge is 1.
- * @return The opacity according to the distance between umbra and penumbra.
- */
-float SpotShadow::calculateOpacity(float layerRatio) {
-    return (layerRatio * layerRatio + layerRatio) / 2.0;
-}
-
-/**
  * Calculate the number of vertex we will create given a number of rays and layers
  *
  * @param rays number of points around the polygons you want
  * @param layers number of layers of triangle strips you need
  * @return number of vertex (multiply by 3 for number of floats)
  */
-int SpotShadow::getStripSize(int rays, int layers) {
-    return  (2 + rays + ((layers) * 2 * (rays + 1)));
+int SpotShadow::getStripSize(int rays) {
+    return  (2 + rays + (2 * (rays + 1)));
 }
 
 #if DEBUG_SHADOW
@@ -898,7 +913,3 @@
 
 }; // namespace uirenderer
 }; // namespace android
-
-
-
-
diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h
index 6727eac..7839dc3 100644
--- a/libs/hwui/SpotShadow.h
+++ b/libs/hwui/SpotShadow.h
@@ -38,9 +38,8 @@
     static void computeLightPolygon(int points, const Vector3& lightCenter,
             float size, Vector3* ret);
 
-    static int  getStripSize(int rays, int layers);
+    static int  getStripSize(int rays);
     static void smoothPolygon(int level, int rays, float* rayDist);
-    static float calculateOpacity(float jf);
     static float rayIntersectPoly(const Vector2* poly, int polyLength,
             const Vector2& point, float dx, float dy);
 
@@ -50,7 +49,6 @@
     static int intersection(Vector2* poly1, int poly1length, Vector2* poly2, int poly2length);
     static void sort(Vector2* poly, int polyLength, const Vector2& center);
 
-    static float angle(const Vector2& point, const Vector2& center);
     static void swap(Vector2* points, int i, int j);
     static void quicksortCirc(Vector2* points, int low, int high, const Vector2& center);
     static void quicksortX(Vector2* points, int low, int high);
@@ -65,8 +63,6 @@
     static void generateTriangleStrip(const Vector2* penumbra, int penumbraLength,
             const Vector2* umbra, int umbraLength, VertexBuffer& retstrips);
 
-    static const double EPSILON = 1e-7;
-
 #if DEBUG_SHADOW
     // Verification utility function.
     static bool testConvex(const Vector2* polygon, int polygonLength,
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index 15b9d6b..c61cb61 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -124,6 +124,10 @@
     Vector3(float px, float py, float pz) :
         x(px), y(py), z(pz) {
     }
+
+    void dump() {
+        ALOGD("Vector3[%.2f, %.2f, %.2f]", x, y, z);
+    }
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 0568851..ce66d8f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -373,7 +373,7 @@
     mCanvas->setViewport(width, height);
 }
 
-void CanvasContext::swapDisplayListData(DisplayList* displayList, DisplayListData* newData) {
+void CanvasContext::setDisplayListData(DisplayList* displayList, DisplayListData* newData) {
     displayList->setData(newData);
 }
 
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 2c9348c..649ffb6 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -63,7 +63,7 @@
     bool initialize(EGLNativeWindowType window);
     void updateSurface(EGLNativeWindowType window);
     void setup(int width, int height);
-    void swapDisplayListData(DisplayList* displayList, DisplayListData* newData);
+    void setDisplayListData(DisplayList* displayList, DisplayListData* newData);
     void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters);
     void drawDisplayList(DisplayList* displayList, Rect* dirty);
     void destroyCanvas();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index c3bf404..200c21f 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -117,14 +117,14 @@
     post(task);
 }
 
-CREATE_BRIDGE3(swapDisplayListData, CanvasContext* context, DisplayList* displayList,
+CREATE_BRIDGE3(setDisplayListData, CanvasContext* context, DisplayList* displayList,
         DisplayListData* newData) {
-    args->context->swapDisplayListData(args->displayList, args->newData);
+    args->context->setDisplayListData(args->displayList, args->newData);
     return NULL;
 }
 
-void RenderProxy::swapDisplayListData(DisplayList* displayList, DisplayListData* newData) {
-    SETUP_TASK(swapDisplayListData);
+void RenderProxy::setDisplayListData(DisplayList* displayList, DisplayListData* newData) {
+    SETUP_TASK(setDisplayListData);
     args->context = mContext;
     args->displayList = displayList;
     args->newData = newData;
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 0934b98..83a8a8f 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -60,7 +60,7 @@
     ANDROID_API bool initialize(EGLNativeWindowType window);
     ANDROID_API void updateSurface(EGLNativeWindowType window);
     ANDROID_API void setup(int width, int height);
-    ANDROID_API void swapDisplayListData(DisplayList* displayList, DisplayListData* newData);
+    ANDROID_API void setDisplayListData(DisplayList* displayList, DisplayListData* newData);
     ANDROID_API void drawDisplayList(DisplayList* displayList,
             int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
     ANDROID_API void destroyCanvas();
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 3759108..5611efb 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -16,8 +16,13 @@
 
 package android.media;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
+import java.nio.NioUtils;
 
+import android.annotation.IntDef;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -145,6 +150,28 @@
     private final static String TAG = "android.media.AudioTrack";
 
 
+    /** @hide */
+    @IntDef({
+        WRITE_BLOCKING,
+        WRITE_NON_BLOCKING
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WriteMode {}
+
+    /**
+     * @hide CANDIDATE FOR PUBLIC API
+     * The write mode indicating the write operation will block until all data has been written,
+     * to be used in {@link #write(ByteBuffer, int, int, int)}.
+     */
+    public final static int WRITE_BLOCKING = 0;
+    /**
+     * @hide CANDIDATE FOR PUBLIC API
+     * The write mode indicating the write operation will return immediately after
+     * queuing as much audio data for playback as possible without blocking, to be used in
+     * {@link #write(ByteBuffer, int, int, int)}.
+     */
+    public final static int WRITE_NON_BLOCKING = 1;
+
     //--------------------------------------------------------------------------
     // Member variables
     //--------------------
@@ -1084,7 +1111,8 @@
             return ERROR_BAD_VALUE;
         }
 
-        int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat);
+        int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,
+                true /*isBlocking*/);
 
         if ((mDataLoadMode == MODE_STATIC)
                 && (mState == STATE_NO_STATIC_DATA)
@@ -1141,6 +1169,75 @@
 
 
     /**
+     * @hide CANDIDATE FOR PUBLIC API
+     * Writes the audio data to the audio sink for playback (streaming mode),
+     * or copies audio data for later playback (static buffer mode).
+     * In static buffer mode, copies the data to the buffer starting at its 0 offset, and the write
+     * mode is ignored.
+     * In streaming mode, the blocking behavior will depend on the write mode.
+     * @param audioData the buffer that holds the data to play, starting at the position reported
+     *     by <code>audioData.position()</code>.
+     *     <BR>Note that this method will not update the position in this buffer, therefore when
+     *     writing a loop to write all the data in the buffer, you should increment the
+     *     <code>offsetInBytes</code> parameter at each pass by the amount that was previously
+     *     written for this buffer.
+     * @param offsetInBytes offset to read from in bytes (note this differs from
+     *     <code>audioData.position()</code>).
+     * @param sizeInBytes number of bytes to read (note this differs from
+     *     <code>audioData.remaining()</code>).
+     * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
+     *     effect in static mode.
+     *     <BR>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
+     *         to the audio sink.
+     *     <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
+     *     queuing as much audio data for playback as possible without blocking.
+     * @return 0 or a positive number of bytes that were written, or
+     *     {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
+     */
+    public int write(ByteBuffer audioData, int offsetInBytes, int sizeInBytes,
+            @WriteMode int writeMode) {
+
+        if (mState == STATE_UNINITIALIZED) {
+            Log.e(TAG, "AudioTrack.write() called in invalid state STATE_UNINITIALIZED");
+            return ERROR_INVALID_OPERATION;
+        }
+
+        if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) {
+            Log.e(TAG, "AudioTrack.write() called with invalid blocking mode");
+            return ERROR_BAD_VALUE;
+        }
+
+        if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0)
+                || (offsetInBytes + sizeInBytes < 0)    // detect integer overflow
+                || (offsetInBytes + sizeInBytes > audioData.remaining())) {
+            Log.e(TAG, "AudioTrack.write() called with invalid size/offset values");
+            return ERROR_BAD_VALUE;
+        }
+
+        int ret = 0;
+        if (audioData.isDirect()) {
+            ret = native_write_native_bytes(audioData,
+                    audioData.position(),
+                    offsetInBytes, sizeInBytes, mAudioFormat,
+                    writeMode == WRITE_BLOCKING);
+        } else {
+            ret = native_write_byte(NioUtils.unsafeArray(audioData),
+                    NioUtils.unsafeArrayOffset(audioData) + audioData.position() + offsetInBytes,
+                    sizeInBytes, mAudioFormat,
+                    writeMode == WRITE_BLOCKING);
+        }
+
+        if ((mDataLoadMode == MODE_STATIC)
+                && (mState == STATE_NO_STATIC_DATA)
+                && (ret > 0)) {
+            // benign race with respect to other APIs that read mState
+            mState = STATE_INITIALIZED;
+        }
+
+        return ret;
+    }
+
+    /**
      * Notifies the native resource to reuse the audio data already loaded in the native
      * layer, that is to rewind to start of buffer.
      * The track's creation mode must be {@link #MODE_STATIC}.
@@ -1339,11 +1436,15 @@
     private native final void native_flush();
 
     private native final int native_write_byte(byte[] audioData,
-                                               int offsetInBytes, int sizeInBytes, int format);
+                                               int offsetInBytes, int sizeInBytes, int format,
+                                               boolean isBlocking);
 
     private native final int native_write_short(short[] audioData,
                                                 int offsetInShorts, int sizeInShorts, int format);
 
+    private native final int native_write_native_bytes(Object audioData,
+            int positionInBytes, int offsetInBytes, int sizeInBytes, int format, boolean blocking);
+
     private native final int native_reload_static();
 
     private native final int native_get_native_frame_count();
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index e5c97e7..f518ab2 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -17,13 +17,11 @@
 package android.media;
 
 import android.media.MediaCodec.BufferInfo;
-
 import dalvik.system.CloseGuard;
 
-import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.util.Map;
 
@@ -79,6 +77,7 @@
         private OutputFormat() {}
         /** MPEG4 media file format*/
         public static final int MUXER_OUTPUT_MPEG_4 = 0;
+        public static final int MUXER_OUTPUT_WEBM   = 1;
     };
 
     // All the native functions are listed here.
@@ -120,20 +119,22 @@
         if (path == null) {
             throw new IllegalArgumentException("path must not be null");
         }
-        if (format != OutputFormat.MUXER_OUTPUT_MPEG_4) {
+        if (format != OutputFormat.MUXER_OUTPUT_MPEG_4 &&
+                format != OutputFormat.MUXER_OUTPUT_WEBM) {
             throw new IllegalArgumentException("format is invalid");
         }
-        FileOutputStream fos = null;
+        // Use RandomAccessFile so we can open the file with RW access;
+        // RW access allows the native writer to memory map the output file.
+        RandomAccessFile file = null;
         try {
-            File file = new File(path);
-            fos = new FileOutputStream(file);
-            FileDescriptor fd = fos.getFD();
+            file = new RandomAccessFile(path, "rws");
+            FileDescriptor fd = file.getFD();
             mNativeObject = nativeSetup(fd, format);
             mState = MUXER_STATE_INITIALIZED;
             mCloseGuard.open("release");
         } finally {
-            if (fos != null) {
-                fos.close();
+            if (file != null) {
+                file.close();
             }
         }
     }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 2806bff..e20a4af 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -3069,7 +3069,7 @@
                 if (refreshTime ||
                         nanoTime >= mLastNanoTime + MAX_NS_WITHOUT_POSITION_CHECK) {
                     try {
-                        mLastTimeUs = mPlayer.getCurrentPosition() * 1000;
+                        mLastTimeUs = mPlayer.getCurrentPosition() * 1000L;
                         mPaused = !mPlayer.isPlaying();
                         if (DEBUG) Log.v(TAG, (mPaused ? "paused" : "playing") + " at " + mLastTimeUs);
                     } catch (IllegalStateException e) {
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index d3a8b22..a78f16d 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -561,7 +561,7 @@
         return JNI_FALSE;
     }
 
-    size_t numSubSamples = size / sizeof(size_t);
+    size_t numSubSamples = size / sizeof(int32_t);
 
     if (numSubSamples == 0) {
         return JNI_FALSE;
@@ -571,7 +571,7 @@
     jboolean isCopy;
     jint *dst = env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
     for (size_t i = 0; i < numSubSamples; ++i) {
-        dst[i] = ((const size_t *)data)[i];
+        dst[i] = ((const int32_t *)data)[i];
     }
     env->ReleaseIntArrayElements(numBytesOfEncryptedDataObj, dst, 0);
     dst = NULL;
@@ -588,7 +588,7 @@
         jboolean isCopy;
         jint *dst = env->GetIntArrayElements(numBytesOfPlainDataObj, &isCopy);
         for (size_t i = 0; i < numSubSamples; ++i) {
-            dst[i] = ((const size_t *)data)[i];
+            dst[i] = ((const int32_t *)data)[i];
         }
         env->ReleaseIntArrayElements(numBytesOfPlainDataObj, dst, 0);
         dst = NULL;
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index f3a7ff7..4e42ae3 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -256,7 +256,7 @@
                         fields.createConfigMethod,
                         SkBitmap::kRGB_565_Config);
 
-    size_t width, height;
+    uint32_t width, height;
     bool swapWidthAndHeight = false;
     if (videoFrame->mRotationAngle == 90 || videoFrame->mRotationAngle == 270) {
         width = videoFrame->mHeight;
@@ -287,8 +287,8 @@
 
     if (videoFrame->mDisplayWidth  != videoFrame->mWidth ||
         videoFrame->mDisplayHeight != videoFrame->mHeight) {
-        size_t displayWidth = videoFrame->mDisplayWidth;
-        size_t displayHeight = videoFrame->mDisplayHeight;
+        uint32_t displayWidth = videoFrame->mDisplayWidth;
+        uint32_t displayHeight = videoFrame->mDisplayHeight;
         if (swapWidthAndHeight) {
             displayWidth = videoFrame->mDisplayHeight;
             displayHeight = videoFrame->mDisplayWidth;
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 0fa27c1..14de6dd 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -58,8 +58,8 @@
     // how many cells the user has to cross before we poke the wakelock
     private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
 
-    private int mFailedPatternAttemptsSinceLastTimeout = 0;
-    private int mTotalFailedPatternAttempts = 0;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
     private CountDownTimer mCountdownTimer = null;
     private LockPatternUtils mLockPatternUtils;
     private LockPatternView mLockPatternView;
@@ -100,6 +100,7 @@
 
     public KeyguardPatternView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
     }
 
     public void setKeyguardCallback(KeyguardSecurityCallback callback) {
@@ -202,7 +203,8 @@
         if (mCallback.isVerifyUnlockOnly()) {
             updateFooter(FooterMode.VerifyUnlocked);
         } else if (mEnableFallback &&
-                (mTotalFailedPatternAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
+                (mKeyguardUpdateMonitor.getFailedUnlockAttempts()
+                        >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
             updateFooter(FooterMode.ForgotLockPattern);
         } else {
             updateFooter(FooterMode.Normal);
@@ -211,7 +213,7 @@
     }
 
     private void displayDefaultSecurityMessage() {
-        if (KeyguardUpdateMonitor.getInstance(mContext).getMaxBiometricUnlockAttemptsReached()) {
+        if (mKeyguardUpdateMonitor.getMaxBiometricUnlockAttemptsReached()) {
             mSecurityMessageDisplay.setMessage(R.string.faceunlock_multiple_failures, true);
         } else {
             mSecurityMessageDisplay.setMessage(R.string.kg_pattern_instructions, false);
@@ -262,20 +264,20 @@
             if (mLockPatternUtils.checkPattern(pattern)) {
                 mCallback.reportUnlockAttempt(true);
                 mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
-                mTotalFailedPatternAttempts = 0;
                 mCallback.dismiss(true);
             } else {
                 if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
                     mCallback.userActivity(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
                 }
                 mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
-                if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
-                    mTotalFailedPatternAttempts++;
-                    mFailedPatternAttemptsSinceLastTimeout++;
+                boolean registeredAttempt =
+                        pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL;
+                if (registeredAttempt) {
                     mCallback.reportUnlockAttempt(false);
                 }
-                if (mFailedPatternAttemptsSinceLastTimeout
-                        >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
+                int attempts = mKeyguardUpdateMonitor.getFailedUnlockAttempts();
+                if (registeredAttempt &&
+                        0 == (attempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
                     long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
                     handleAttemptLockout(deadline);
                 } else {
@@ -363,7 +365,6 @@
                 mLockPatternView.setEnabled(true);
                 displayDefaultSecurityMessage();
                 // TODO mUnlockIcon.setVisibility(View.VISIBLE);
-                mFailedPatternAttemptsSinceLastTimeout = 0;
                 if (mEnableFallback) {
                     updateFooter(FooterMode.ForgotLockPattern);
                 } else {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 8d6fe41..b09cc1d 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -52,6 +52,7 @@
     <uses-permission android:name="android.permission.START_ANY_ACTIVITY" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
 
     <!-- WindowManager -->
     <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
@@ -140,6 +141,18 @@
             </intent-filter>
         </receiver>
 
+        <!-- Alternate Recents -->
+        <activity android:name=".recents.RecentsActivity"
+                  android:launchMode="singleInstance"
+                  android:excludeFromRecents="true"
+                  android:theme="@style/RecentsTheme">
+            <intent-filter>
+                <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
+            </intent-filter>
+        </activity>
+
+        <service android:name=".recents.RecentsService" />
+
         <!-- started from UsbDeviceSettingsManager -->
         <activity android:name=".usb.UsbConfirmActivity"
             android:exported="true"
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 1ff93d2..48d9722 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -6,6 +6,11 @@
   public void setGlowAlpha(float);
   public void setGlowScale(float);
 }
+-keep class com.android.systemui.recents.views.TaskIconView {
+	public void setCircularClipRadius(float);
+	public float getCircularClipRadius();
+}
 
 -keep class com.android.systemui.statusbar.phone.PhoneStatusBar
 -keep class com.android.systemui.statusbar.tv.TvStatusBar
+-keep class com.android.systemui.recents.*
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
new file mode 100644
index 0000000..4bd7e82
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false"
+     android:zAdjustment="top">
+  <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+         android:fillEnabled="true"
+         android:fillBefore="true" android:fillAfter="true"
+         android:interpolator="@android:interpolator/accelerate_cubic"
+         android:duration="250"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
new file mode 100644
index 0000000..becc9d0
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false"
+     android:zAdjustment="normal">
+  <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+         android:fillEnabled="true"
+         android:fillBefore="true" android:fillAfter="true"
+         android:interpolator="@android:interpolator/decelerate_cubic"
+         android:duration="250"/>
+</set>
diff --git a/packages/SystemUI/res/layout/recents_empty.xml b/packages/SystemUI/res/layout/recents_empty.xml
new file mode 100644
index 0000000..6268628
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_empty.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    android:textSize="40sp"
+    android:textColor="#ffffffff"
+    android:text="@string/recents_empty_message"
+    android:fontFamily="sans-serif-thin"
+    android:visibility="gone" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 1e6011c..f0db449 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -198,7 +198,7 @@
     <string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ikke forbundet"</string>
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Intet netværk"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi slået fra"</string>
-    <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast-skærm"</string>
+    <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast skærm"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Lysstyrke"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="1666358784283020762">"Farveinverteringstilstand"</string>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 94796af..ce05639 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -507,6 +507,9 @@
     <!-- QuickSettings: Label for the toggle that controls whether display color correction is enabled. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_color_space_label">Color correction mode</string>
 
+    <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
+    <string name="recents_empty_message">RECENTS</string>
+
 
     <!-- Glyph to be overlaid atop the battery when the level is extremely low. Do not translate. -->
     <string name="battery_meter_very_low_overlay_symbol">!</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 54f03bd..14af020 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -20,6 +20,13 @@
         <item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item>
     </style>
 
+    <!-- Alternate Recents theme -->
+    <style name="RecentsTheme" parent="@android:style/Theme.Holo.Wallpaper.NoTitleBar">
+        <item name="android:windowTranslucentStatus">true</item>
+        <item name="android:windowTranslucentNavigation">true</item>
+        <item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item>
+    </style>
+
     <!-- Animations for a non-full-screen window or activity. -->
     <style name="Animation.RecentsActivity" parent="@android:style/Animation.Activity">
         <item name="android:activityOpenEnterAnimation">@anim/recents_launch_from_launcher_enter</item>
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Recents.java b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
index f5670e1..07c0c78 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
@@ -16,37 +16,143 @@
 
 package com.android.systemui.recent;
 
+import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceControl;
 import android.view.View;
-
+import android.view.WindowManager;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SystemUI;
 
+import java.util.List;
+
+
 public class Recents extends SystemUI implements RecentsComponent {
+    /** A handler for messages from the recents implementation */
+    class RecentsMessageHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            if (!mUseAlternateRecents) return;
+            if (msg.what == MSG_UPDATE_FOR_CONFIGURATION) {
+                Resources res = mContext.getResources();
+                float statusBarHeight = res.getDimensionPixelSize(
+                        com.android.internal.R.dimen.status_bar_height);
+                mFirstTaskRect = (Rect) msg.getData().getParcelable("taskRect");
+                mFirstTaskRect.offset(0, (int) statusBarHeight);
+            }
+        }
+    }
+
+    /** A service connection to the recents implementation */
+    class RecentsServiceConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            if (!mUseAlternateRecents) return;
+
+            Log.d(TAG, "[RecentsComponent|ServiceConnection|onServiceConnected] toggleRecents: " +
+                    mToggleRecentsUponServiceBound);
+            mService = new Messenger(service);
+            mServiceIsBound = true;
+
+            // Toggle recents if this service connection was triggered by hitting the recents button
+            if (mToggleRecentsUponServiceBound) {
+                startAlternateRecentsActivity();
+            }
+            mToggleRecentsUponServiceBound = false;
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName className) {
+            if (!mUseAlternateRecents) return;
+
+            Log.d(TAG, "[RecentsComponent|ServiceConnection|onServiceDisconnected]");
+            mService = null;
+            mServiceIsBound = false;
+        }
+    }
+
     private static final String TAG = "Recents";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
+
+    final static int MSG_UPDATE_FOR_CONFIGURATION = 0;
+    final static int MSG_UPDATE_TASK_THUMBNAIL = 1;
+    final static int MSG_PRELOAD_TASKS = 2;
+    final static int MSG_CANCEL_PRELOAD_TASKS = 3;
+
+    final static String sToggleRecentsAction = "com.android.systemui.recents.TOGGLE_RECENTS";
+    final static String sRecentsPackage = "com.android.systemui";
+    final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
+    final static String sRecentsService = "com.android.systemui.recents.RecentsService";
+
+    // Which recents to use
+    boolean mUseAlternateRecents;
+
+    // Recents service binding
+    Messenger mService = null;
+    Messenger mMessenger;
+    boolean mServiceIsBound = false;
+    boolean mToggleRecentsUponServiceBound;
+    RecentsServiceConnection mConnection = new RecentsServiceConnection();
+
+    View mStatusBarView;
+    Rect mFirstTaskRect = new Rect();
+
+    public Recents() {
+        mMessenger = new Messenger(new RecentsMessageHandler());
+    }
 
     @Override
     public void start() {
+        mUseAlternateRecents =
+                SystemProperties.getBoolean("persist.recents.use_alternate", false);
+
         putComponent(RecentsComponent.class, this);
+
+        if (mUseAlternateRecents) {
+            Log.d(TAG, "[RecentsComponent|start]");
+
+            // Try to create a long-running connection to the recents service
+            bindToRecentsService(false);
+        }
     }
 
     @Override
     public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
+        if (mUseAlternateRecents) {
+            // Launch the alternate recents if required
+            toggleAlternateRecents(display, layoutDirection, statusBarView);
+            return;
+        }
+
         if (DEBUG) Log.d(TAG, "toggle recents panel");
         try {
             TaskDescription firstTask = RecentTasksLoader.getInstance(mContext).getFirstTask();
@@ -190,33 +296,227 @@
         }
     }
 
+    /** Toggles the alternate recents activity */
+    public void toggleAlternateRecents(Display display, int layoutDirection, View statusBarView) {
+        if (!mUseAlternateRecents) return;
+
+        Log.d(TAG, "[RecentsComponent|toggleRecents] serviceIsBound: " + mServiceIsBound);
+        mStatusBarView = statusBarView;
+        if (!mServiceIsBound) {
+            // Try to create a long-running connection to the recents service before toggling
+            // recents
+            bindToRecentsService(true);
+            return;
+        }
+
+        try {
+            startAlternateRecentsActivity();
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "Failed to launch RecentAppsIntent", e);
+        }
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        if (mServiceIsBound) {
+            Resources res = mContext.getResources();
+            int statusBarHeight = res.getDimensionPixelSize(
+                    com.android.internal.R.dimen.status_bar_height);
+            int navBarHeight = res.getDimensionPixelSize(
+                    com.android.internal.R.dimen.navigation_bar_height);
+            Rect rect = new Rect();
+            WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+            wm.getDefaultDisplay().getRectSize(rect);
+
+            // Try and update the recents configuration
+            try {
+                Bundle data = new Bundle();
+                data.putParcelable("windowRect", rect);
+                data.putParcelable("systemInsets", new Rect(0, statusBarHeight, 0, 0));
+                Message msg = Message.obtain(null, MSG_UPDATE_FOR_CONFIGURATION, 0, 0);
+                msg.setData(data);
+                msg.replyTo = mMessenger;
+                mService.send(msg);
+            } catch (RemoteException re) {
+                re.printStackTrace();
+            }
+        }
+    }
+
+    /** Binds to the recents implementation */
+    private void bindToRecentsService(boolean toggleRecentsUponConnection) {
+        if (!mUseAlternateRecents) return;
+
+        mToggleRecentsUponServiceBound = toggleRecentsUponConnection;
+        Intent intent = new Intent();
+        intent.setClassName(sRecentsPackage, sRecentsService);
+        mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    /** Loads the first task thumbnail */
+    Bitmap loadFirstTaskThumbnail() {
+        ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        List<ActivityManager.RecentTaskInfo> tasks = am.getRecentTasksForUser(1,
+                ActivityManager.RECENT_IGNORE_UNAVAILABLE, UserHandle.CURRENT.getIdentifier());
+        for (ActivityManager.RecentTaskInfo t : tasks) {
+            // Skip tasks in the home stack
+            if (am.isInHomeStack(t.persistentId)) {
+                return null;
+            }
+
+            Bitmap thumbnail = am.getTaskTopThumbnail(t.persistentId);
+            return thumbnail;
+        }
+        return null;
+    }
+
+    /** Returns whether there is a first task */
+    boolean hasFirstTask() {
+        ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        List<ActivityManager.RecentTaskInfo> tasks = am.getRecentTasksForUser(1,
+                ActivityManager.RECENT_IGNORE_UNAVAILABLE, UserHandle.CURRENT.getIdentifier());
+        for (ActivityManager.RecentTaskInfo t : tasks) {
+            // Skip tasks in the home stack
+            if (am.isInHomeStack(t.persistentId)) {
+                continue;
+            }
+
+            return true;
+        }
+        return false;
+    }
+
+    /** Converts from the device rotation to the degree */
+    float getDegreesForRotation(int value) {
+        switch (value) {
+            case Surface.ROTATION_90:
+                return 360f - 90f;
+            case Surface.ROTATION_180:
+                return 360f - 180f;
+            case Surface.ROTATION_270:
+                return 360f - 270f;
+        }
+        return 0f;
+    }
+
+    /** Takes a screenshot of the surface */
+    Bitmap takeScreenshot(Display display) {
+        DisplayMetrics dm = new DisplayMetrics();
+        display.getRealMetrics(dm);
+        float[] dims = {dm.widthPixels, dm.heightPixels};
+        float degrees = getDegreesForRotation(display.getRotation());
+        boolean requiresRotation = (degrees > 0);
+        if (requiresRotation) {
+            // Get the dimensions of the device in its native orientation
+            Matrix m = new Matrix();
+            m.preRotate(-degrees);
+            m.mapPoints(dims);
+            dims[0] = Math.abs(dims[0]);
+            dims[1] = Math.abs(dims[1]);
+        }
+        return SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
+    }
+
+    /** Starts the recents activity */
+    void startAlternateRecentsActivity() {
+        Rect taskRect = mFirstTaskRect;
+        if (taskRect != null && taskRect.width() > 0 && taskRect.height() > 0 && hasFirstTask()) {
+            // Loading from thumbnail
+            Bitmap thumbnail;
+            Bitmap firstThumbnail = loadFirstTaskThumbnail();
+            if (firstThumbnail == null) {
+                // Load the thumbnail from the screenshot
+                WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+                Display display = wm.getDefaultDisplay();
+                Bitmap screenshot = takeScreenshot(display);
+                Resources res = mContext.getResources();
+                int size = Math.min(screenshot.getWidth(), screenshot.getHeight());
+                int statusBarHeight = res.getDimensionPixelSize(
+                        com.android.internal.R.dimen.status_bar_height);
+                thumbnail = Bitmap.createBitmap(mFirstTaskRect.width(), mFirstTaskRect.height(),
+                        Bitmap.Config.ARGB_8888);
+                Canvas c = new Canvas(thumbnail);
+                c.drawBitmap(screenshot, new Rect(0, statusBarHeight, size, statusBarHeight + size),
+                        new Rect(0, 0, mFirstTaskRect.width(), mFirstTaskRect.height()), null);
+                c.setBitmap(null);
+                // Recycle the old screenshot
+                screenshot.recycle();
+            } else {
+                // Create the thumbnail
+                thumbnail = Bitmap.createBitmap(mFirstTaskRect.width(), mFirstTaskRect.height(),
+                        Bitmap.Config.ARGB_8888);
+                int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight());
+                Canvas c = new Canvas(thumbnail);
+                c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size),
+                        new Rect(0, 0, mFirstTaskRect.width(), mFirstTaskRect.height()), null);
+                c.setBitmap(null);
+                // Recycle the old thumbnail
+                firstThumbnail.recycle();
+            }
+
+            ActivityOptions opts = ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView,
+                    thumbnail, mFirstTaskRect.left, mFirstTaskRect.top, null);
+            startAlternateRecentsActivity(opts);
+        } else {
+            ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
+                    R.anim.recents_from_launcher_enter,
+                    R.anim.recents_from_launcher_exit);
+            startAlternateRecentsActivity(opts);
+        }
+    }
+
+    /** Starts the recents activity */
+    void startAlternateRecentsActivity(ActivityOptions opts) {
+        Intent intent = new Intent(sToggleRecentsAction);
+        intent.setClassName(sRecentsPackage, sRecentsActivity);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        if (opts != null) {
+            mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
+                    UserHandle.USER_CURRENT));
+        } else {
+            mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+        }
+    }
+
     @Override
     public void preloadRecentTasksList() {
-        if (DEBUG) Log.d(TAG, "preloading recents");
-        Intent intent = new Intent(RecentsActivity.PRELOAD_INTENT);
-        intent.setClassName("com.android.systemui",
-                "com.android.systemui.recent.RecentsPreloadReceiver");
-        mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+        if (mUseAlternateRecents) {
+            Log.d(TAG, "[RecentsComponent|preloadRecents]");
+        } else {
+            Intent intent = new Intent(RecentsActivity.PRELOAD_INTENT);
+            intent.setClassName("com.android.systemui",
+                    "com.android.systemui.recent.RecentsPreloadReceiver");
+            mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
 
-        RecentTasksLoader.getInstance(mContext).preloadFirstTask();
+            RecentTasksLoader.getInstance(mContext).preloadFirstTask();
+        }
     }
 
     @Override
     public void cancelPreloadingRecentTasksList() {
-        if (DEBUG) Log.d(TAG, "cancel preloading recents");
-        Intent intent = new Intent(RecentsActivity.CANCEL_PRELOAD_INTENT);
-        intent.setClassName("com.android.systemui",
-                "com.android.systemui.recent.RecentsPreloadReceiver");
-        mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+        if (mUseAlternateRecents) {
+            Log.d(TAG, "[RecentsComponent|cancelPreload]");
+        } else {
+            Intent intent = new Intent(RecentsActivity.CANCEL_PRELOAD_INTENT);
+            intent.setClassName("com.android.systemui",
+                    "com.android.systemui.recent.RecentsPreloadReceiver");
+            mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
 
-        RecentTasksLoader.getInstance(mContext).cancelPreloadingFirstTask();
+            RecentTasksLoader.getInstance(mContext).cancelPreloadingFirstTask();
+        }
     }
 
     @Override
     public void closeRecents() {
-        if (DEBUG) Log.d(TAG, "closing recents panel");
-        Intent intent = new Intent(RecentsActivity.CLOSE_RECENTS_INTENT);
-        intent.setPackage("com.android.systemui");
-        mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+        if (mUseAlternateRecents) {
+            Log.d(TAG, "[RecentsComponent|closeRecents]");
+        } else {
+            Intent intent = new Intent(RecentsActivity.CLOSE_RECENTS_INTENT);
+            intent.setPackage("com.android.systemui");
+            mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+
+            RecentTasksLoader.getInstance(mContext).cancelPreloadingFirstTask();
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Console.java b/packages/SystemUI/src/com/android/systemui/recents/Console.java
new file mode 100644
index 0000000..b3d9ccf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/Console.java
@@ -0,0 +1,96 @@
+/*
+ * 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.recents;
+
+
+import android.content.Context;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.widget.Toast;
+
+public class Console {
+    // Colors
+    public static final String AnsiReset = "\u001B[0m";
+    public static final String AnsiBlack = "\u001B[30m";
+    public static final String AnsiRed = "\u001B[31m";      // SystemUIHandshake
+    public static final String AnsiGreen = "\u001B[32m";    // MeasureAndLayout
+    public static final String AnsiYellow = "\u001B[33m";   // SynchronizeViewsWithModel
+    public static final String AnsiBlue = "\u001B[34m";     // TouchEvents
+    public static final String AnsiPurple = "\u001B[35m";   // Draw
+    public static final String AnsiCyan = "\u001B[36m";     // ClickEvents
+    public static final String AnsiWhite = "\u001B[37m";
+
+    /** Logs a key */
+    public static void log(String key) {
+        Console.log(true, key, "", AnsiReset);
+    }
+
+    /** Logs a conditioned key */
+    public static void log(boolean condition, String key) {
+        if (condition) {
+            Console.log(condition, key, "", AnsiReset);
+        }
+    }
+
+    /** Logs a key in a specific color */
+    public static void log(boolean condition, String key, Object data) {
+        if (condition) {
+            Console.log(condition, key, data, AnsiReset);
+        }
+    }
+
+    /** Logs a key with data in a specific color */
+    public static void log(boolean condition, String key, Object data, String color) {
+        if (condition) {
+            System.out.println(color + key + AnsiReset + " " + data.toString());
+        }
+    }
+
+    /** Logs an error */
+    public static void logError(Context context, String msg) {
+        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
+        Log.e("Recents", msg);
+    }
+
+    /** Logs a divider bar */
+    public static void logDivider(boolean condition) {
+        if (condition) {
+            System.out.println("==== [" + System.currentTimeMillis() +
+                    "] ============================================================");
+        }
+    }
+
+    /** Returns the stringified MotionEvent action */
+    public static String motionEventActionToString(int action) {
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                return "Down";
+            case MotionEvent.ACTION_UP:
+                return "Up";
+            case MotionEvent.ACTION_MOVE:
+                return "Move";
+            case MotionEvent.ACTION_CANCEL:
+                return "Cancel";
+            case MotionEvent.ACTION_POINTER_DOWN:
+                return "Pointer Down";
+            case MotionEvent.ACTION_POINTER_UP:
+                return "Pointer Up";
+            default:
+                return "" + action;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
new file mode 100644
index 0000000..aeae4ab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -0,0 +1,111 @@
+/*
+ * 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.recents;
+
+/**
+ * Constants
+ * XXX: We are going to move almost all of these into a resource.
+ */
+public class Constants {
+    public static class DebugFlags {
+        // Enable this with any other debug flag to see more info
+        public static final boolean Verbose = false;
+
+        public static class App {
+            public static final boolean EnableTaskFiltering = false;
+            public static final boolean EnableTaskStackClipping = false;
+            public static final boolean EnableBackgroundTaskLoading = true;
+            public static final boolean ForceDisableBackgroundCache = false;
+            public static final boolean TaskDataLoader = false;
+            public static final boolean SystemUIHandshake = false;
+            public static final boolean TimeSystemCalls = false;
+        }
+
+        public static class UI {
+            public static final boolean Draw = false;
+            public static final boolean ClickEvents = false;
+            public static final boolean TouchEvents = false;
+            public static final boolean MeasureAndLayout = false;
+            public static final boolean Clipping = false;
+            public static final boolean HwLayers = true;
+        }
+
+        public static class TaskStack {
+            public static final boolean SynchronizeViewsWithModel = false;
+        }
+
+        public static class ViewPool {
+            public static final boolean PoolCallbacks = false;
+        }
+    }
+
+    public static class Values {
+        public static class Window {
+            public static final float DarkBackgroundDim = 0.5f;
+            public static final float BackgroundDim = 0.35f;
+        }
+
+        public static class RecentsTaskLoader {
+            // XXX: This should be calculated on the first load
+            public static final int PreloadFirstTasksCount = 5;
+            public static final int TaskEntryMultiplier = 1;
+        }
+
+        public static class TaskStackView {
+            public static class Animation {
+                public static final int TaskRemovedReshuffleDuration = 200;
+                public static final int SnapScrollBackDuration = 650;
+                public static final int SwipeDismissDuration = 350;
+                public static final int SwipeSnapBackDuration = 350;
+            }
+
+            // The padding will be applied to the smallest dimension, and then applied to all sides
+            public static final float StackPaddingPct = 0.15f;
+            // The overlap height relative to the task height
+            public static final float StackOverlapPct = 0.65f;
+            // The height of the peek space relative to the stack height
+            public static final float StackPeekHeightPct = 0.1f;
+            // The min scale of the last card in the peek area
+            public static final float StackPeekMinScale = 0.9f;
+            // The number of cards we see in the peek space
+            public static final int StackPeekNumCards = 3;
+        }
+
+        public static class TaskView {
+            public static class Animation {
+                public static final int TaskDataUpdatedFadeDuration = 250;
+                public static final int TaskIconCircularClipInDuration = 225;
+                public static final int TaskIconCircularClipOutDuration = 85;
+            }
+
+            public static final boolean AnimateFrontTaskIconOnEnterRecents = true;
+            public static final boolean AnimateFrontTaskIconOnLeavingRecents = true;
+            public static final boolean AnimateFrontTaskIconOnLeavingUseClip = false;
+            public static final boolean DrawColoredTaskBars = false;
+            public static final boolean UseRoundedCorners = true;
+            public static final float RoundedCornerRadiusDps = 3;
+
+            public static final float TaskBarHeightDps = 54;
+            public static final float TaskIconSizeDps = 60;
+        }
+    }
+
+    // UNMIGRATED CONSTANTS:
+
+    /** Determines whether to layout the stack vertically in landscape mode */
+    public static final boolean LANDSCAPE_LAYOUT_VERTICAL_STACK = true;
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
new file mode 100644
index 0000000..d050847
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -0,0 +1,176 @@
+/*
+ * 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.recents;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import com.android.systemui.recents.model.SpaceNode;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.RecentsView;
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+
+
+/* Activity */
+public class RecentsActivity extends Activity {
+    FrameLayout mContainerView;
+    RecentsView mRecentsView;
+    View mEmptyView;
+    boolean mVisible;
+
+    /** Updates the set of recent tasks */
+    void updateRecentsTasks() {
+        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+        SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
+        ArrayList<TaskStack> stacks = root.getStacks();
+        if (!stacks.isEmpty()) {
+            // XXX: We just replace the root every time for now, we will change this in the future
+            mRecentsView.setBSP(root);
+        }
+
+        // Add the default no-recents layout
+        if (stacks.size() == 1 && stacks.get(0).getTaskCount() == 0) {
+            mEmptyView.setVisibility(View.VISIBLE);
+
+            // Dim the background even more
+            WindowManager.LayoutParams wlp = getWindow().getAttributes();
+            wlp.dimAmount = Constants.Values.Window.DarkBackgroundDim;
+            getWindow().setAttributes(wlp);
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+        } else {
+            mEmptyView.setVisibility(View.GONE);
+        }
+    }
+
+    /** Dismisses recents if we are already visible and the intent is to toggle the recents view */
+    boolean dismissRecentsIfVisible(Intent intent) {
+        if ("com.android.systemui.recents.TOGGLE_RECENTS".equals(intent.getAction())) {
+            if (mVisible) {
+                if (!mRecentsView.launchFirstTask()) {
+                    finish();
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Called with the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Console.logDivider(Constants.DebugFlags.App.SystemUIHandshake);
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onCreate]",
+                getIntent().getAction() + " visible: " + mVisible, Console.AnsiRed);
+
+        // Initialize the loader and the configuration
+        RecentsTaskLoader.initialize(this);
+        RecentsConfiguration.reinitialize(this);
+
+        // Dismiss recents if it is visible and we are toggling
+        if (dismissRecentsIfVisible(getIntent())) return;
+
+        // Set the background dim
+        WindowManager.LayoutParams wlp = getWindow().getAttributes();
+        wlp.dimAmount = Constants.Values.Window.BackgroundDim;
+        getWindow().setAttributes(wlp);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+
+        // Create the view hierarchy
+        mRecentsView = new RecentsView(this);
+        mRecentsView.setLayoutParams(new FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.MATCH_PARENT,
+                FrameLayout.LayoutParams.MATCH_PARENT));
+
+        // Create the empty view
+        LayoutInflater inflater = LayoutInflater.from(this);
+        mEmptyView = inflater.inflate(R.layout.recents_empty, mContainerView, false);
+
+        mContainerView = new FrameLayout(this);
+        mContainerView.addView(mRecentsView);
+        mContainerView.addView(mEmptyView);
+        setContentView(mContainerView);
+
+        // Update the recent tasks
+        updateRecentsTasks();
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        Console.logDivider(Constants.DebugFlags.App.SystemUIHandshake);
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onNewIntent]",
+                intent.getAction() + " visible: " + mVisible, Console.AnsiRed);
+
+        // Dismiss recents if it is visible and we are toggling
+        if (dismissRecentsIfVisible(intent)) return;
+
+        // Initialize the loader and the configuration
+        RecentsTaskLoader.initialize(this);
+        RecentsConfiguration.reinitialize(this);
+
+        // Update the recent tasks
+        updateRecentsTasks();
+    }
+
+    @Override
+    protected void onStart() {
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onStart]", "",
+                Console.AnsiRed);
+        super.onStart();
+        mVisible = true;
+    }
+
+    @Override
+    protected void onResume() {
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onResume]", "",
+                Console.AnsiRed);
+        super.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onPause]", "",
+                Console.AnsiRed);
+        super.onPause();
+
+        // Stop the loader when we leave Recents
+        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+        loader.stopLoader();
+    }
+
+    @Override
+    protected void onStop() {
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onStop]", "",
+                Console.AnsiRed);
+        super.onStop();
+        mVisible = false;
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (!mRecentsView.unfilterFilteredStacks()) {
+            super.onBackPressed();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
new file mode 100644
index 0000000..f3881ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -0,0 +1,76 @@
+/*
+ * 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.recents;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+
+
+/** A static Recents configuration for the current context
+ * NOTE: We should not hold any references to a Context from a static instance */
+public class RecentsConfiguration {
+    static RecentsConfiguration sInstance;
+
+    DisplayMetrics mDisplayMetrics;
+
+    public boolean layoutVerticalStack;
+    public Rect systemInsets = new Rect();
+
+    /** Private constructor */
+    private RecentsConfiguration() {}
+
+    /** Updates the configuration to the current context */
+    public static RecentsConfiguration reinitialize(Context context) {
+        if (sInstance == null) {
+            sInstance = new RecentsConfiguration();
+        }
+        sInstance.update(context);
+        return sInstance;
+    }
+
+    /** Returns the current recents configuration */
+    public static RecentsConfiguration getInstance() {
+        return sInstance;
+    }
+
+    /** Updates the state, given the specified context */
+    void update(Context context) {
+        mDisplayMetrics = context.getResources().getDisplayMetrics();
+
+        boolean isPortrait = context.getResources().getConfiguration().orientation ==
+                Configuration.ORIENTATION_PORTRAIT;
+        layoutVerticalStack = isPortrait || Constants.LANDSCAPE_LAYOUT_VERTICAL_STACK;
+    }
+
+    public void updateSystemInsets(Rect insets) {
+        systemInsets.set(insets);
+    }
+
+    /** Converts from DPs to PXs */
+    public int pxFromDp(float size) {
+        return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                size, mDisplayMetrics));
+    }
+    /** Converts from SPs to PXs */
+    public int pxFromSp(float size) {
+        return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+                size, mDisplayMetrics));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
new file mode 100644
index 0000000..522ab0f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
@@ -0,0 +1,103 @@
+/*
+ * 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.recents;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.TaskStackView;
+import com.android.systemui.recents.views.TaskViewTransform;
+
+
+/* Service */
+public class RecentsService extends Service {
+    // XXX: This should be getting the message from recents definition
+    final static int MSG_UPDATE_RECENTS_FOR_CONFIGURATION = 0;
+
+    class MessageHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsService|handleMessage]", msg);
+            if (msg.what == MSG_UPDATE_RECENTS_FOR_CONFIGURATION) {
+                Context context = RecentsService.this;
+                RecentsTaskLoader.initialize(context);
+                RecentsConfiguration.reinitialize(context);
+
+                try {
+                    Bundle data = msg.getData();
+                    Rect windowRect = (Rect) data.getParcelable("windowRect");
+                    Rect systemInsets = (Rect) data.getParcelable("systemInsets");
+                    RecentsConfiguration.getInstance().updateSystemInsets(systemInsets);
+
+                    // Create a dummy task stack & compute the rect for the thumbnail to animate to
+                    TaskStack stack = new TaskStack(context);
+                    TaskStackView tsv = new TaskStackView(context, stack);
+                    tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top);
+                    tsv.boundScroll();
+                    TaskViewTransform transform = tsv.getStackTransform(0);
+
+                    data.putParcelable("taskRect", transform.rect);
+                    Message reply = Message.obtain(null, MSG_UPDATE_RECENTS_FOR_CONFIGURATION, 0, 0);
+                    reply.setData(data);
+                    msg.replyTo.send(reply);
+                } catch (RemoteException re) {
+                    re.printStackTrace();
+                }
+            }
+        }
+    }
+
+    Messenger mMessenger = new Messenger(new MessageHandler());
+
+    @Override
+    public void onCreate() {
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsService|onCreate]");
+        super.onCreate();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsService|onBind]");
+        return mMessenger.getBinder();
+    }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsService|onUnbind]");
+        return super.onUnbind(intent);
+    }
+
+    @Override
+    public void onRebind(Intent intent) {
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsService|onRebind]");
+        super.onRebind(intent);
+    }
+
+    @Override
+    public void onDestroy() {
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsService|onDestroy]");
+        super.onDestroy();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
new file mode 100644
index 0000000..c303ca7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -0,0 +1,463 @@
+/*
+ * 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.recents;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.UserHandle;
+import android.util.LruCache;
+import com.android.systemui.recents.model.SpaceNode;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+
+/** A bitmap load queue */
+class TaskResourceLoadQueue {
+    ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
+
+    Task nextTask() {
+        Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|nextTask]");
+        return mQueue.poll();
+    }
+
+    void addTask(Task t) {
+        Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|addTask]");
+        if (!mQueue.contains(t)) {
+            mQueue.add(t);
+        }
+        synchronized(this) {
+            notifyAll();
+        }
+    }
+
+    void removeTask(Task t) {
+        Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|removeTask]");
+        mQueue.remove(t);
+    }
+
+    void clearTasks() {
+        Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|clearTasks]");
+        mQueue.clear();
+    }
+
+    boolean isEmpty() {
+        return mQueue.isEmpty();
+    }
+}
+
+/* Task resource loader */
+class TaskResourceLoader implements Runnable {
+    Context mContext;
+    HandlerThread mLoadThread;
+    Handler mLoadThreadHandler;
+    Handler mMainThreadHandler;
+
+    TaskResourceLoadQueue mLoadQueue;
+    DrawableLruCache mIconCache;
+    BitmapLruCache mThumbnailCache;
+    boolean mCancelled;
+
+    /** Constructor, creates a new loading thread that loads task resources in the background */
+    public TaskResourceLoader(TaskResourceLoadQueue loadQueue, DrawableLruCache iconCache,
+                              BitmapLruCache thumbnailCache) {
+        mLoadQueue = loadQueue;
+        mIconCache = iconCache;
+        mThumbnailCache = thumbnailCache;
+        mMainThreadHandler = new Handler();
+        mLoadThread = new HandlerThread("Recents-TaskResourceLoader");
+        mLoadThread.setPriority(Thread.NORM_PRIORITY - 1);
+        mLoadThread.start();
+        mLoadThreadHandler = new Handler(mLoadThread.getLooper());
+        mLoadThreadHandler.post(this);
+    }
+
+    /** Restarts the loader thread */
+    void start(Context context) {
+        Console.log(Constants.DebugFlags.App.TaskDataLoader, "[TaskResourceLoader|start]");
+        mContext = context;
+        mCancelled = false;
+        // Notify the load thread to start loading
+        synchronized(mLoadThread) {
+            mLoadThread.notifyAll();
+        }
+    }
+
+    /** Requests the loader thread to stop after the current iteration */
+    void stop() {
+        Console.log(Constants.DebugFlags.App.TaskDataLoader, "[TaskResourceLoader|stop]");
+        // Mark as cancelled for the thread to pick up
+        mCancelled = true;
+    }
+
+    @Override
+    public void run() {
+        while (true) {
+            Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                    "[TaskResourceLoader|run|" + Thread.currentThread().getId() + "]");
+            if (mCancelled) {
+                // We have to unset the context here, since the background thread may be using it
+                // when we call stop()
+                mContext = null;
+                // If we are cancelled, then wait until we are started again
+                synchronized(mLoadThread) {
+                    try {
+                        Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                                "[TaskResourceLoader|waitOnLoadThreadCancelled]");
+                        mLoadThread.wait();
+                    } catch (InterruptedException ie) {
+                        ie.printStackTrace();
+                    }
+                }
+            } else {
+                // Load the next item from the queue
+                final Task t = mLoadQueue.nextTask();
+                if (t != null) {
+                    try {
+                        Drawable cachedIcon = mIconCache.get(t);
+                        Bitmap cachedThumbnail = mThumbnailCache.get(t);
+                        Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                                "  [TaskResourceLoader|load]",
+                                t + " icon: " + cachedIcon + " thumbnail: " + cachedThumbnail);
+                        // Load the icon
+                        if (cachedIcon == null) {
+                            PackageManager pm = mContext.getPackageManager();
+                            ActivityInfo info = pm.getActivityInfo(t.intent.getComponent(),
+                                    PackageManager.GET_META_DATA);
+                            Drawable icon = info.loadIcon(pm);
+                            if (!mCancelled) {
+                                Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                                        "    [TaskResourceLoader|loadIcon]",
+                                        icon);
+                                t.icon = icon;
+                                mIconCache.put(t, icon);
+                            }
+                        }
+                        // Load the thumbnail
+                        if (cachedThumbnail == null) {
+                            ActivityManager am = (ActivityManager)
+                                    mContext.getSystemService(Context.ACTIVITY_SERVICE);
+                            Bitmap thumbnail = am.getTaskTopThumbnail(t.id);
+                            if (!mCancelled) {
+                                if (thumbnail != null) {
+                                    Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                                            "    [TaskResourceLoader|loadThumbnail]",
+                                            thumbnail);
+                                    t.thumbnail = thumbnail;
+                                    mThumbnailCache.put(t, thumbnail);
+                                } else {
+                                    Console.logError(mContext,
+                                            "Failed to load task top thumbnail for: " +
+                                                    t.intent.getComponent().getPackageName());
+                                }
+                            }
+                        }
+                        if (!mCancelled) {
+                            // Notify that the task data has changed
+                            mMainThreadHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    t.notifyTaskDataChanged();
+                                }
+                            });
+                        }
+                    } catch (PackageManager.NameNotFoundException ne) {
+                        ne.printStackTrace();
+                    }
+                }
+
+                // If there are no other items in the list, then just wait until something is added
+                if (!mCancelled && mLoadQueue.isEmpty()) {
+                    synchronized(mLoadQueue) {
+                        try {
+                            Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                                    "[TaskResourceLoader|waitOnLoadQueue]");
+                            mLoadQueue.wait();
+                        } catch (InterruptedException ie) {
+                            ie.printStackTrace();
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+/** The drawable cache */
+class DrawableLruCache extends LruCache<Task, Drawable> {
+    public DrawableLruCache(int cacheSize) {
+        super(cacheSize);
+    }
+
+    @Override
+    protected int sizeOf(Task t, Drawable d) {
+        // The cache size will be measured in kilobytes rather than number of items
+        // NOTE: this isn't actually correct, as the icon may be smaller
+        int maxBytes = (d.getIntrinsicWidth() * d.getIntrinsicHeight() * 4);
+        return maxBytes / 1024;
+    }
+}
+
+/** The bitmap cache */
+class BitmapLruCache extends LruCache<Task, Bitmap> {
+    public BitmapLruCache(int cacheSize) {
+        super(cacheSize);
+    }
+
+    @Override
+    protected int sizeOf(Task t, Bitmap bitmap) {
+        // The cache size will be measured in kilobytes rather than number of items
+        return bitmap.getByteCount() / 1024;
+    }
+}
+
+/* Recents task loader
+ * NOTE: We should not hold any references to a Context from a static instance */
+public class RecentsTaskLoader {
+    static RecentsTaskLoader sInstance;
+
+    DrawableLruCache mIconCache;
+    BitmapLruCache mThumbnailCache;
+    TaskResourceLoadQueue mLoadQueue;
+    TaskResourceLoader mLoader;
+
+    BitmapDrawable mDefaultIcon;
+    Bitmap mDefaultThumbnail;
+
+    /** Private Constructor */
+    private RecentsTaskLoader(Context context) {
+        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
+        int iconCacheSize = Constants.DebugFlags.App.ForceDisableBackgroundCache ? 1 : maxMemory / 16;
+        int thumbnailCacheSize = Constants.DebugFlags.App.ForceDisableBackgroundCache ? 1 : maxMemory / 8;
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                "[RecentsTaskLoader|init]", "thumbnailCache: " + thumbnailCacheSize +
+                " iconCache: " + iconCacheSize);
+        mLoadQueue = new TaskResourceLoadQueue();
+        mIconCache = new DrawableLruCache(iconCacheSize);
+        mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
+        mLoader = new TaskResourceLoader(mLoadQueue, mIconCache, mThumbnailCache);
+
+        // Create the default assets
+        Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+        mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+        Canvas c = new Canvas();
+        c.setBitmap(icon);
+        c.drawColor(0x00000000);
+        c.setBitmap(mDefaultThumbnail);
+        c.drawColor(0x00000000);
+        c.setBitmap(null);
+        mDefaultIcon = new BitmapDrawable(context.getResources(), icon);
+        Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                "[RecentsTaskLoader|defaultBitmaps]",
+                "icon: " + mDefaultIcon + " thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
+    }
+
+    /** Initializes the recents task loader */
+    public static RecentsTaskLoader initialize(Context context) {
+        if (sInstance == null) {
+            sInstance = new RecentsTaskLoader(context);
+        }
+        return sInstance;
+    }
+
+    /** Returns the current recents task loader */
+    public static RecentsTaskLoader getInstance() {
+        return sInstance;
+    }
+
+    /** Reload the set of recent tasks */
+    SpaceNode reload(Context context, int preloadCount) {
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsTaskLoader|reload]");
+        TaskStack stack = new TaskStack(context);
+        SpaceNode root = new SpaceNode(context);
+        root.setStack(stack);
+        try {
+            long t1 = System.currentTimeMillis();
+
+            PackageManager pm = context.getPackageManager();
+            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+
+            // Get the recent tasks
+            List<ActivityManager.RecentTaskInfo> tasks = am.getRecentTasksForUser(25,
+                    ActivityManager.RECENT_IGNORE_UNAVAILABLE, UserHandle.CURRENT.getIdentifier());
+            Collections.reverse(tasks);
+            Console.log(Constants.DebugFlags.App.TimeSystemCalls,
+                    "[RecentsTaskLoader|getRecentTasks]",
+                    "" + (System.currentTimeMillis() - t1) + "ms");
+            Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                    "[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 (am.isInHomeStack(t.persistentId)) {
+                    iter.remove();
+                    continue;
+                }
+                // Skip tasks from this Recents package
+                if (t.baseIntent.getComponent().getPackageName().equals(context.getPackageName())) {
+                    iter.remove();
+                    continue;
+                }
+            }
+
+            // Add each task to the task stack
+            t1 = System.currentTimeMillis();
+            int taskCount = tasks.size();
+            for (int i = 0; i < taskCount; i++) {
+                ActivityManager.RecentTaskInfo t = tasks.get(i);
+
+                // Load the label, icon and thumbnail
+                ActivityInfo info = pm.getActivityInfo(t.baseIntent.getComponent(),
+                        PackageManager.GET_META_DATA);
+                String title = info.loadLabel(pm).toString();
+                Drawable icon = null;
+                Bitmap thumbnail = null;
+                Task task;
+                if (i >= (taskCount - preloadCount) || !Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
+                    Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                            "[RecentsTaskLoader|preloadTask]",
+                            "i: " + i + " task: " + t.baseIntent.getComponent().getPackageName());
+                    icon = info.loadIcon(pm);
+                    thumbnail = am.getTaskTopThumbnail(t.id);
+                    for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) {
+                        Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                                "  [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
+                        task = new Task(t.persistentId, t.baseIntent, title, icon, thumbnail);
+                        if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
+                            if (thumbnail != null) mThumbnailCache.put(task, thumbnail);
+                            if (icon != null) {
+                                mIconCache.put(task, icon);
+                            }
+                        }
+                        stack.addTask(task);
+                    }
+                } else {
+                    for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) {
+                        Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                                "  [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
+                        task = new Task(t.persistentId, t.baseIntent, title, null, null);
+                        stack.addTask(task);
+                    }
+                }
+
+                /*
+                if (stacks.containsKey(t.stackId)) {
+                    builder = stacks.get(t.stackId);
+                } else {
+                    builder = new TaskStackBuilder();
+                    stacks.put(t.stackId, builder);
+                }
+                */
+            }
+            Console.log(Constants.DebugFlags.App.TimeSystemCalls,
+                    "[RecentsTaskLoader|getAllTaskTopThumbnail]",
+                    "" + (System.currentTimeMillis() - t1) + "ms");
+
+            /*
+            // Get all the stacks
+            t1 = System.currentTimeMillis();
+            List<ActivityManager.StackInfo> stackInfos = ams.getAllStackInfos();
+            Console.log(Constants.DebugFlags.App.TimeSystemCalls, "[RecentsTaskLoader|getAllStackInfos]", "" + (System.currentTimeMillis() - t1) + "ms");
+            Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsTaskLoader|stacks]", "" + tasks.size());
+            for (ActivityManager.StackInfo s : stackInfos) {
+                Console.log(Constants.DebugFlags.App.SystemUIHandshake, "  [RecentsTaskLoader|stack]", s.toString());
+                if (stacks.containsKey(s.stackId)) {
+                    stacks.get(s.stackId).setRect(s.bounds);
+                }
+            }
+            */
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        mLoader.start(context);
+        return root;
+    }
+
+    /** Acquires the task resource data from the pool.
+     * XXX: Move this into Task? */
+    public void loadTaskData(Task t) {
+        if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
+            t.icon = mIconCache.get(t);
+            t.thumbnail = mThumbnailCache.get(t);
+
+            Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]",
+                    t + " icon: " + t.icon + " thumbnail: " + t.thumbnail);
+
+            boolean requiresLoad = false;
+            if (t.icon == null) {
+                t.icon = mDefaultIcon;
+                requiresLoad = true;
+            }
+            if (t.thumbnail == null) {
+                t.thumbnail = mDefaultThumbnail;
+                requiresLoad = true;
+            }
+            if (requiresLoad) {
+                mLoadQueue.addTask(t);
+            }
+        }
+    }
+
+    /** Releases the task resource data back into the pool.
+     * XXX: Move this into Task? */
+    public void unloadTaskData(Task t) {
+        if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
+            Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                    "[RecentsTaskLoader|unloadTask]", t);
+            mLoadQueue.removeTask(t);
+            t.icon = mDefaultIcon;
+            t.thumbnail = mDefaultThumbnail;
+        }
+    }
+
+    /** Completely removes the resource data from the pool.
+     * XXX: Move this into Task? */
+    public void deleteTaskData(Task t) {
+        if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
+            Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                    "[RecentsTaskLoader|deleteTask]", t);
+            mLoadQueue.removeTask(t);
+            mThumbnailCache.remove(t);
+            mIconCache.remove(t);
+        }
+        t.icon = mDefaultIcon;
+        t.thumbnail = mDefaultThumbnail;
+    }
+
+    /** Stops the task loader */
+    void stopLoader() {
+        Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|stopLoader]");
+        mLoader.stop();
+        mLoadQueue.clearTasks();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
new file mode 100644
index 0000000..33e4246
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
@@ -0,0 +1,39 @@
+/*
+ * 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.recents;
+
+import android.graphics.Rect;
+
+/* Common code */
+public class Utilities {
+    public static final Rect tmpRect = new Rect();
+    public static final Rect tmpRect2 = new Rect();
+
+    /** Scales a rect about its centroid */
+    public static void scaleRectAboutCenter(Rect r, float scale) {
+        if (scale != 1.0f) {
+            int cx = r.centerX();
+            int cy = r.centerY();
+            r.offset(-cx, -cy);
+            r.left = (int) (r.left * scale + 0.5f);
+            r.top = (int) (r.top * scale + 0.5f);
+            r.right = (int) (r.right * scale + 0.5f);
+            r.bottom = (int) (r.bottom * scale + 0.5f);
+            r.offset(cx, cy);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java b/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
new file mode 100644
index 0000000..5893abc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
@@ -0,0 +1,69 @@
+/*
+ * 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.recents.model;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+
+
+/**
+ * The full recents space is partitioned using a BSP into various nodes that define where task
+ * stacks should be placed.
+ */
+public class SpaceNode {
+    Context mContext;
+
+    SpaceNode mStartNode;
+    SpaceNode mEndNode;
+
+    TaskStack mStack;
+
+    public SpaceNode(Context context) {
+        mContext = context;
+    }
+
+    /** Sets the current stack for this space node */
+    public void setStack(TaskStack stack) {
+        mStack = stack;
+    }
+
+    /** Returns the task stack (not null if this is a leaf) */
+    TaskStack getStack() {
+        return mStack;
+    }
+
+    /** Returns whether this is a leaf node */
+    boolean isLeafNode() {
+        return (mStartNode == null) && (mEndNode == null);
+    }
+
+    /** Returns all the descendent task stacks */
+    private void getStacksRec(ArrayList<TaskStack> stacks) {
+        if (isLeafNode()) {
+            stacks.add(mStack);
+        } else {
+            mStartNode.getStacksRec(stacks);
+            mEndNode.getStacksRec(stacks);
+        }
+    }
+    public ArrayList<TaskStack> getStacks() {
+        ArrayList<TaskStack> stacks = new ArrayList<TaskStack>();
+        getStacksRec(stacks);
+        return stacks;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNodeCallbacks.java b/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNodeCallbacks.java
new file mode 100644
index 0000000..31b02e7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNodeCallbacks.java
@@ -0,0 +1,28 @@
+/*
+ * 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.recents.model;
+
+import android.graphics.Rect;
+
+
+/* BSP node callbacks */
+public interface SpaceNodeCallbacks {
+    /** Notifies when a node is added */
+    public void onSpaceNodeAdded(SpaceNode node);
+    /** Notifies when a node is measured */
+    public void onSpaceNodeMeasured(SpaceNode node, Rect rect);
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
new file mode 100644
index 0000000..9b03c5d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -0,0 +1,76 @@
+/*
+ * 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.recents.model;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import com.android.systemui.recents.Constants;
+
+
+/**
+ * A task represents the top most task in the system's task stack.
+ */
+public class Task {
+    public final int id;
+    public final Intent intent;
+    public String title;
+    public Drawable icon;
+    public Bitmap thumbnail;
+
+    TaskCallbacks mCb;
+
+    public Task(int id, Intent intent, String activityTitle, Drawable icon, Bitmap thumbnail) {
+        this.id = id;
+        this.intent = intent;
+        this.title = activityTitle;
+        this.icon = icon;
+        this.thumbnail = thumbnail;
+    }
+
+    /** Set the callbacks */
+    public void setCallbacks(TaskCallbacks cb) {
+        mCb = cb;
+    }
+
+    /** Notifies the callback listeners that this task's data has changed */
+    public void notifyTaskDataChanged() {
+        if (mCb != null) {
+            mCb.onTaskDataChanged(this);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        // If we have multiple task entries for the same task, then we do the simple object
+        // equality check
+        if (Constants.Values.RecentsTaskLoader.TaskEntryMultiplier > 1) {
+            return super.equals(o);
+        }
+
+        // Otherwise, check that the id and intent match (the other fields can be asynchronously
+        // loaded and is unsuitable to testing the identity of this Task)
+        Task t = (Task) o;
+        return (id == t.id) &&
+                (intent.equals(t.intent));
+    }
+
+    @Override
+    public String toString() {
+        return "Task: " + intent.getComponent().getPackageName() + " [" + super.toString() + "]";
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskCallbacks.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskCallbacks.java
new file mode 100644
index 0000000..169f56c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskCallbacks.java
@@ -0,0 +1,23 @@
+/*
+ * 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.recents.model;
+
+/* Task callbacks */
+public interface TaskCallbacks {
+    /* Notifies when a task's data has been updated */
+    public void onTaskDataChanged(Task task);
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
new file mode 100644
index 0000000..a5aa387
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -0,0 +1,231 @@
+/*
+ * 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.recents.model;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * An interface for a task filter to query whether a particular task should show in a stack.
+ */
+interface TaskFilter {
+    /** Returns whether the filter accepts the specified task */
+    public boolean acceptTask(Task t, int index);
+}
+
+/**
+ * A list of filtered tasks.
+ */
+class FilteredTaskList {
+    ArrayList<Task> mTasks = new ArrayList<Task>();
+    ArrayList<Task> mFilteredTasks = new ArrayList<Task>();
+    TaskFilter mFilter;
+
+    /** Sets the task filter, saving the current touch state */
+    void setFilter(TaskFilter filter) {
+        mFilter = filter;
+        updateFilteredTasks();
+    }
+
+    /** Removes the task filter and returns the previous touch state */
+    void removeFilter() {
+        mFilter = null;
+        updateFilteredTasks();
+    }
+
+    /** Adds a new task to the task list */
+    void add(Task t) {
+        mTasks.add(t);
+        updateFilteredTasks();
+    }
+
+    /** Sets the list of tasks */
+    void set(List<Task> tasks) {
+        mTasks.clear();
+        mTasks.addAll(tasks);
+        updateFilteredTasks();
+    }
+
+    /** Removes a task from the base list only if it is in the filtered list */
+    boolean remove(Task t) {
+        if (mFilteredTasks.contains(t)) {
+            boolean removed = mTasks.remove(t);
+            updateFilteredTasks();
+            return removed;
+        }
+        return false;
+    }
+
+    /** Returns the index of this task in the list of filtered tasks */
+    int indexOf(Task t) {
+        return mFilteredTasks.indexOf(t);
+    }
+
+    /** Returns the size of the list of filtered tasks */
+    int size() {
+        return mFilteredTasks.size();
+    }
+
+    /** Returns whether the filtered list contains this task */
+    boolean contains(Task t) {
+        return mFilteredTasks.contains(t);
+    }
+
+    /** Updates the list of filtered tasks whenever the base task list changes */
+    private void updateFilteredTasks() {
+        mFilteredTasks.clear();
+        if (mFilter != null) {
+            int taskCount = mTasks.size();
+            for (int i = 0; i < taskCount; i++) {
+                Task t = mTasks.get(i);
+                if (mFilter.acceptTask(t, i)) {
+                    mFilteredTasks.add(t);
+                }
+            }
+        } else {
+            mFilteredTasks.addAll(mTasks);
+        }
+    }
+
+    /** Returns whether this task list is filtered */
+    boolean hasFilter() {
+        return (mFilter != null);
+    }
+
+    /** Returns the list of filtered tasks */
+    ArrayList<Task> getTasks() {
+        return mFilteredTasks;
+    }
+}
+
+/**
+ * The task stack contains a list of multiple tasks.
+ */
+public class TaskStack {
+    Context mContext;
+
+    FilteredTaskList mTaskList = new FilteredTaskList();
+    TaskStackCallbacks mCb;
+
+    public TaskStack(Context context) {
+        mContext = context;
+    }
+
+    /** Sets the callbacks for this task stack */
+    public void setCallbacks(TaskStackCallbacks cb) {
+        mCb = cb;
+    }
+
+    /** Adds a new task */
+    public void addTask(Task t) {
+        mTaskList.add(t);
+        if (mCb != null) {
+            mCb.onStackTaskAdded(this, t);
+        }
+    }
+
+    /** Removes a task */
+    public void removeTask(Task t) {
+        if (mTaskList.contains(t)) {
+            mTaskList.remove(t);
+            if (mCb != null) {
+                mCb.onStackTaskRemoved(this, t);
+            }
+        }
+    }
+
+    /** Sets a few tasks in one go */
+    public void setTasks(List<Task> tasks) {
+        int taskCount = mTaskList.getTasks().size();
+        for (int i = 0; i < taskCount; i++) {
+            Task t = mTaskList.getTasks().get(i);
+            if (mCb != null) {
+                mCb.onStackTaskRemoved(this, t);
+            }
+        }
+        mTaskList.set(tasks);
+        for (Task t : tasks) {
+            if (mCb != null) {
+                mCb.onStackTaskAdded(this, t);
+            }
+        }
+    }
+
+    /** Gets the tasks */
+    public ArrayList<Task> getTasks() {
+        return mTaskList.getTasks();
+    }
+
+    /** Gets the number of tasks */
+    public int getTaskCount() {
+        return mTaskList.size();
+    }
+
+    /** Returns the index of this task in this current task stack */
+    public int indexOfTask(Task t) {
+        return mTaskList.indexOf(t);
+    }
+
+    /** Tests whether a task is in this current task stack */
+    public boolean containsTask(Task t) {
+        return mTaskList.contains(t);
+    }
+
+    /** Filters the stack into tasks similar to the one specified */
+    public void filterTasks(Task t) {
+        // Set the task list filter
+        // XXX: This is a dummy filter that currently just accepts every other task.
+        mTaskList.setFilter(new TaskFilter() {
+            @Override
+            public boolean acceptTask(Task t, int i) {
+                if (i % 2 == 0) {
+                    return true;
+                }
+                return false;
+            }
+        });
+        if (mCb != null) {
+            mCb.onStackFiltered(this);
+        }
+    }
+
+    /** Unfilters the current stack */
+    public void unfilterTasks() {
+        // Unset the filter, then update the virtual scroll
+        mTaskList.removeFilter();
+        if (mCb != null) {
+            mCb.onStackUnfiltered(this);
+        }
+    }
+
+    /** Returns whether tasks are currently filtered */
+    public boolean hasFilteredTasks() {
+        return mTaskList.hasFilter();
+    }
+
+    @Override
+    public String toString() {
+        String str = "Tasks:\n";
+        for (Task t : mTaskList.getTasks()) {
+            str += "  " + t.toString() + "\n";
+        }
+        return str;
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStackCallbacks.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStackCallbacks.java
new file mode 100644
index 0000000..4bec655
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStackCallbacks.java
@@ -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.
+ */
+
+package com.android.systemui.recents.model;
+
+/* Task stack callbacks */
+public interface TaskStackCallbacks {
+    /* Notifies when a task has been added to the stack */
+    public void onStackTaskAdded(TaskStack stack, Task t);
+    /* Notifies when a task has been removed from the stack */
+    public void onStackTaskRemoved(TaskStack stack, Task t);
+    /** Notifies when the stack was filtered */
+    public void onStackFiltered(TaskStack stack);
+    /** Notifies when the stack was un-filtered */
+    public void onStackUnfiltered(TaskStack stack);
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
new file mode 100644
index 0000000..c92041c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -0,0 +1,228 @@
+/*
+ * 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.recents.views;
+
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.UserHandle;
+import android.view.View;
+import android.widget.FrameLayout;
+import com.android.systemui.recents.Console;
+import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.model.SpaceNode;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+
+import java.util.ArrayList;
+
+
+/**
+ * This view is the the top level layout that contains TaskStacks (which are laid out according
+ * to their SpaceNode bounds.
+ */
+public class RecentsView extends FrameLayout implements TaskStackViewCallbacks {
+    // The space partitioning root of this container
+    SpaceNode mBSP;
+
+    public RecentsView(Context context) {
+        super(context);
+        setWillNotDraw(false);
+    }
+
+    /** Set/get the bsp root node */
+    public void setBSP(SpaceNode n) {
+        mBSP = n;
+
+        // XXX: We shouldn't be recereating new stacks every time, but for now, that is OK
+        // Add all the stacks for this partition
+        removeAllViews();
+        ArrayList<TaskStack> stacks = mBSP.getStacks();
+        for (TaskStack stack : stacks) {
+            TaskStackView stackView = new TaskStackView(getContext(), stack);
+            stackView.setCallbacks(this);
+            addView(stackView);
+        }
+    }
+
+    /** Launches the first task from the first stack if possible */
+    public boolean launchFirstTask() {
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            TaskStackView stackView = (TaskStackView) getChildAt(i);
+            TaskStack stack = stackView.mStack;
+            ArrayList<Task> tasks = stack.getTasks();
+            if (!tasks.isEmpty()) {
+                Task task = tasks.get(tasks.size() - 1);
+                TaskView tv = null;
+                if (stackView.getChildCount() > 0) {
+                    TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1);
+                    if (stv.getTask() == task) {
+                        tv = stv;
+                    }
+                }
+                onTaskLaunched(stackView, tv, stack, task);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        int height = MeasureSpec.getSize(heightMeasureSpec);
+        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|measure]", "width: " + width + " height: " + height, Console.AnsiGreen);
+
+        // We measure our stack views sans the status bar.  It will handle the nav bar itself.
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+        int childHeight = height - config.systemInsets.top;
+
+        // Measure each child
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                child.measure(widthMeasureSpec,
+                        MeasureSpec.makeMeasureSpec(childHeight, heightMode));
+            }
+        }
+
+        setMeasuredDimension(width, height);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|layout]", new Rect(left, top, right, bottom) + " changed: " + changed, Console.AnsiGreen);
+        // We offset our stack views by the status bar height.  It will handle the nav bar itself.
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+        top += config.systemInsets.top;
+
+        // Layout each child
+        // XXX: Based on the space node for that task view
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                final int width = child.getMeasuredWidth();
+                final int height = child.getMeasuredHeight();
+                child.layout(left, top, left + width, top + height);
+            }
+        }
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        Console.log(Constants.DebugFlags.UI.Draw, "[RecentsView|dispatchDraw]", "", Console.AnsiPurple);
+        super.dispatchDraw(canvas);
+    }
+
+    @Override
+    protected boolean fitSystemWindows(Rect insets) {
+        Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen);
+
+        // Update the configuration with the latest system insets and trigger a relayout
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+        config.updateSystemInsets(insets);
+        requestLayout();
+
+        return true;
+    }
+
+    /** Unfilters any filtered stacks */
+    public boolean unfilterFilteredStacks() {
+        if (mBSP != null) {
+            // Check if there are any filtered stacks and unfilter them before we back out of Recents
+            boolean stacksUnfiltered = false;
+            ArrayList<TaskStack> stacks = mBSP.getStacks();
+            for (TaskStack stack : stacks) {
+                if (stack.hasFilteredTasks()) {
+                    stack.unfilterTasks();
+                    stacksUnfiltered = true;
+                }
+            }
+            return stacksUnfiltered;
+        }
+        return false;
+    }
+
+    /**** View.OnClickListener Implementation ****/
+
+    @Override
+    public void onTaskLaunched(final TaskStackView stackView, final TaskView tv,
+                               final TaskStack stack, final Task task) {
+        final Runnable launchRunnable = new Runnable() {
+            @Override
+            public void run() {
+                TaskViewTransform transform;
+                View sourceView = tv;
+                int offsetX = 0;
+                int offsetY = 0;
+                if (tv == null) {
+                    // Launch the activity
+                    sourceView = stackView;
+                    transform = stackView.getStackTransform(stack.indexOfTask(task));
+                    offsetX = transform.rect.left;
+                    offsetY = transform.rect.top;
+                } else {
+                    transform = stackView.getStackTransform(stack.indexOfTask(task));
+                }
+
+                // Compute the thumbnail to scale up from
+                ActivityOptions opts = null;
+                int thumbnailWidth = transform.rect.width();
+                int thumbnailHeight = transform.rect.height();
+                if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
+                        task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
+                    // Resize the thumbnail to the size of the view that we are animating from
+                    Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
+                            Bitmap.Config.ARGB_8888);
+                    Canvas c = new Canvas(b);
+                    c.drawBitmap(task.thumbnail,
+                            new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
+                            new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
+                    c.setBitmap(null);
+                    opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
+                            b, offsetX, offsetY);
+                }
+
+                // Launch the activity with the desired animation
+                Intent i = new Intent(task.intent);
+                i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+                        | Intent.FLAG_ACTIVITY_TASK_ON_HOME
+                        | Intent.FLAG_ACTIVITY_NEW_TASK);
+                if (opts != null) {
+                    getContext().startActivityAsUser(i, opts.toBundle(), UserHandle.CURRENT);
+                } else {
+                    getContext().startActivityAsUser(i, UserHandle.CURRENT);
+                }
+            }
+        };
+
+        // Launch the app right away if there is no task view, otherwise, animate the icon out first
+        if (tv == null || !Constants.Values.TaskView.AnimateFrontTaskIconOnLeavingRecents) {
+            launchRunnable.run();
+        } else {
+            tv.animateOnLeavingRecents(launchRunnable);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
new file mode 100644
index 0000000..fe661bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -0,0 +1,389 @@
+/*
+ * 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.recents.views;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.animation.LinearInterpolator;
+
+/**
+ * This class facilitates swipe to dismiss. It defines an interface to be implemented by the
+ * by the class hosting the views that need to swiped, and, using this interface, handles touch
+ * events and translates / fades / animates the view as it is dismissed.
+ */
+public class SwipeHelper {
+    static final String TAG = "SwipeHelper";
+    private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
+    private static final boolean CONSTRAIN_SWIPE = true;
+    private static final boolean FADE_OUT_DURING_SWIPE = true;
+    private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
+
+    public static final int X = 0;
+    public static final int Y = 1;
+
+    private static LinearInterpolator sLinearInterpolator = new LinearInterpolator();
+
+    private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
+    private int DEFAULT_ESCAPE_ANIMATION_DURATION = 75; // ms
+    private int MAX_ESCAPE_ANIMATION_DURATION = 150; // ms
+    private int MAX_DISMISS_VELOCITY = 2000; // dp/sec
+    private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms
+
+    public static float ALPHA_FADE_START = 0.15f; // fraction of thumbnail width
+                                                 // where fade starts
+    static final float ALPHA_FADE_END = 0.65f; // fraction of thumbnail width
+                                              // beyond which alpha->0
+    private float mMinAlpha = 0f;
+
+    private float mPagingTouchSlop;
+    Callback mCallback;
+    private int mSwipeDirection;
+    private VelocityTracker mVelocityTracker;
+
+    private float mInitialTouchPos;
+    private boolean mDragging;
+
+    private View mCurrView;
+    private boolean mCanCurrViewBeDimissed;
+    private float mDensityScale;
+
+    public boolean mAllowSwipeTowardsStart = true;
+    public boolean mAllowSwipeTowardsEnd = true;
+    private boolean mRtl;
+
+    public SwipeHelper(int swipeDirection, Callback callback, float densityScale,
+            float pagingTouchSlop) {
+        mCallback = callback;
+        mSwipeDirection = swipeDirection;
+        mVelocityTracker = VelocityTracker.obtain();
+        mDensityScale = densityScale;
+        mPagingTouchSlop = pagingTouchSlop;
+    }
+
+    public void setDensityScale(float densityScale) {
+        mDensityScale = densityScale;
+    }
+
+    public void setPagingTouchSlop(float pagingTouchSlop) {
+        mPagingTouchSlop = pagingTouchSlop;
+    }
+
+    public void cancelOngoingDrag() {
+        if (mDragging) {
+            if (mCurrView != null) {
+                mCallback.onDragCancelled(mCurrView);
+                setTranslation(mCurrView, 0);
+                mCallback.onSnapBackCompleted(mCurrView);
+                mCurrView = null;
+            }
+            mDragging = false;
+        }
+    }
+
+    public void resetTranslation(View v) {
+        setTranslation(v, 0);
+    }
+
+    private float getPos(MotionEvent ev) {
+        return mSwipeDirection == X ? ev.getX() : ev.getY();
+    }
+
+    private float getTranslation(View v) {
+        return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY();
+    }
+
+    private float getVelocity(VelocityTracker vt) {
+        return mSwipeDirection == X ? vt.getXVelocity() :
+                vt.getYVelocity();
+    }
+
+    private ObjectAnimator createTranslationAnimation(View v, float newPos) {
+        ObjectAnimator anim = ObjectAnimator.ofFloat(v,
+                mSwipeDirection == X ? View.TRANSLATION_X : View.TRANSLATION_Y, newPos);
+        return anim;
+    }
+
+    private float getPerpendicularVelocity(VelocityTracker vt) {
+        return mSwipeDirection == X ? vt.getYVelocity() :
+                vt.getXVelocity();
+    }
+
+    private void setTranslation(View v, float translate) {
+        if (mSwipeDirection == X) {
+            v.setTranslationX(translate);
+        } else {
+            v.setTranslationY(translate);
+        }
+    }
+
+    private float getSize(View v) {
+        final DisplayMetrics dm = v.getContext().getResources().getDisplayMetrics();
+        return mSwipeDirection == X ? dm.widthPixels : dm.heightPixels;
+    }
+
+    public void setMinAlpha(float minAlpha) {
+        mMinAlpha = minAlpha;
+    }
+
+    float getAlphaForOffset(View view) {
+        float viewSize = getSize(view);
+        final float fadeSize = ALPHA_FADE_END * viewSize;
+        float result = 1.0f;
+        float pos = getTranslation(view);
+        if (pos >= viewSize * ALPHA_FADE_START) {
+            result = 1.0f - (pos - viewSize * ALPHA_FADE_START) / fadeSize;
+        } else if (pos < viewSize * (1.0f - ALPHA_FADE_START)) {
+            result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
+        }
+        result = Math.min(result, 1.0f);
+        result = Math.max(result, 0f);
+        return Math.max(mMinAlpha, result);
+    }
+
+    /**
+     * Determines whether the given view has RTL layout.
+     */
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+    public static boolean isLayoutRtl(View view) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return View.LAYOUT_DIRECTION_RTL == view.getLayoutDirection();
+        } else {
+            return false;
+        }
+    }
+
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        final int action = ev.getAction();
+
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                mDragging = false;
+                mCurrView = mCallback.getChildAtPosition(ev);
+                mVelocityTracker.clear();
+                if (mCurrView != null) {
+                    mRtl = isLayoutRtl(mCurrView);
+                    mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
+                    mVelocityTracker.addMovement(ev);
+                    mInitialTouchPos = getPos(ev);
+                } else {
+                    mCanCurrViewBeDimissed = false;
+                }
+                break;
+            case MotionEvent.ACTION_MOVE:
+                if (mCurrView != null) {
+                    mVelocityTracker.addMovement(ev);
+                    float pos = getPos(ev);
+                    float delta = pos - mInitialTouchPos;
+                    if (Math.abs(delta) > mPagingTouchSlop) {
+                        mCallback.onBeginDrag(mCurrView);
+                        mDragging = true;
+                        mInitialTouchPos = getPos(ev) - getTranslation(mCurrView);
+                    }
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mDragging = false;
+                mCurrView = null;
+                break;
+        }
+        return mDragging;
+    }
+
+    /**
+     * @param view The view to be dismissed
+     * @param velocity The desired pixels/second speed at which the view should move
+     */
+    private void dismissChild(final View view, float velocity) {
+        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
+        float newPos;
+        if (velocity < 0
+                || (velocity == 0 && getTranslation(view) < 0)
+                // if we use the Menu to dismiss an item in landscape, animate up
+                || (velocity == 0 && getTranslation(view) == 0 && mSwipeDirection == Y)) {
+            newPos = -getSize(view);
+        } else {
+            newPos = getSize(view);
+        }
+        int duration = MAX_ESCAPE_ANIMATION_DURATION;
+        if (velocity != 0) {
+            duration = Math.min(duration,
+                                (int) (Math.abs(newPos - getTranslation(view)) *
+                                        1000f / Math.abs(velocity)));
+        } else {
+            duration = DEFAULT_ESCAPE_ANIMATION_DURATION;
+        }
+
+        ValueAnimator anim = createTranslationAnimation(view, newPos);
+        anim.setInterpolator(sLinearInterpolator);
+        anim.setDuration(duration);
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mCallback.onChildDismissed(view);
+                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
+                    view.setAlpha(1.f);
+                }
+            }
+        });
+        anim.addUpdateListener(new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
+                    view.setAlpha(getAlphaForOffset(view));
+                }
+            }
+        });
+        anim.start();
+    }
+
+    private void snapChild(final View view, float velocity) {
+        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
+        ValueAnimator anim = createTranslationAnimation(view, 0);
+        int duration = SNAP_ANIM_LEN;
+        anim.setDuration(duration);
+        anim.addUpdateListener(new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
+                    view.setAlpha(getAlphaForOffset(view));
+                }
+            }
+        });
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
+                    view.setAlpha(1.0f);
+                }
+                mCallback.onSnapBackCompleted(view);
+            }
+        });
+        anim.start();
+    }
+
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (!mDragging) {
+            if (!onInterceptTouchEvent(ev)) {
+                return mCanCurrViewBeDimissed;
+            }
+        }
+
+        mVelocityTracker.addMovement(ev);
+        final int action = ev.getAction();
+        switch (action) {
+            case MotionEvent.ACTION_OUTSIDE:
+            case MotionEvent.ACTION_MOVE:
+                if (mCurrView != null) {
+                    float delta = getPos(ev) - mInitialTouchPos;
+                    setSwipeAmount(delta);
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                if (mCurrView != null) {
+                    endSwipe(mVelocityTracker);
+                }
+                break;
+        }
+        return true;
+    }
+
+    private void setSwipeAmount(float amount) {
+        // don't let items that can't be dismissed be dragged more than
+        // maxScrollDistance
+        if (CONSTRAIN_SWIPE
+                && (!isValidSwipeDirection(amount) || !mCallback.canChildBeDismissed(mCurrView))) {
+            float size = getSize(mCurrView);
+            float maxScrollDistance = 0.15f * size;
+            if (Math.abs(amount) >= size) {
+                amount = amount > 0 ? maxScrollDistance : -maxScrollDistance;
+            } else {
+                amount = maxScrollDistance * (float) Math.sin((amount/size)*(Math.PI/2));
+            }
+        }
+        setTranslation(mCurrView, amount);
+        if (FADE_OUT_DURING_SWIPE && mCanCurrViewBeDimissed) {
+            float alpha = getAlphaForOffset(mCurrView);
+            mCurrView.setAlpha(alpha);
+        }
+    }
+
+    private boolean isValidSwipeDirection(float amount) {
+        if (mSwipeDirection == X) {
+            if (mRtl) {
+                return (amount <= 0) ? mAllowSwipeTowardsEnd : mAllowSwipeTowardsStart;
+            } else {
+                return (amount <= 0) ? mAllowSwipeTowardsStart : mAllowSwipeTowardsEnd;
+            }
+        }
+
+        // Vertical swipes are always valid.
+        return true;
+    }
+
+    private void endSwipe(VelocityTracker velocityTracker) {
+        float maxVelocity = MAX_DISMISS_VELOCITY * mDensityScale;
+        velocityTracker.computeCurrentVelocity(1000 /* px/sec */, maxVelocity);
+        float velocity = getVelocity(velocityTracker);
+        float perpendicularVelocity = getPerpendicularVelocity(velocityTracker);
+        float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
+        float translation = getTranslation(mCurrView);
+        // Decide whether to dismiss the current view
+        boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
+                Math.abs(translation) > 0.6 * getSize(mCurrView);
+        boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
+                (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
+                (velocity > 0) == (translation > 0);
+
+        boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
+                && isValidSwipeDirection(translation)
+                && (childSwipedFastEnough || childSwipedFarEnough);
+
+        if (dismissChild) {
+            // flingadingy
+            dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
+        } else {
+            // snappity
+            mCallback.onDragCancelled(mCurrView);
+            snapChild(mCurrView, velocity);
+        }
+    }
+
+    public interface Callback {
+        View getChildAtPosition(MotionEvent ev);
+
+        boolean canChildBeDismissed(View v);
+
+        void onBeginDrag(View v);
+
+        void onChildDismissed(View v);
+
+        void onSnapBackCompleted(View v);
+
+        void onDragCancelled(View v);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
new file mode 100644
index 0000000..9dd6c0b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -0,0 +1,1075 @@
+/*
+ * 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.recents.views;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewParent;
+import android.widget.FrameLayout;
+import android.widget.OverScroller;
+import android.widget.Toast;
+import com.android.systemui.recents.Console;
+import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.RecentsTaskLoader;
+import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.model.TaskStackCallbacks;
+
+import java.util.ArrayList;
+
+/** The TaskView callbacks */
+interface TaskStackViewCallbacks {
+    public void onTaskLaunched(TaskStackView stackView, TaskView tv, TaskStack stack, Task t);
+}
+
+/* The visual representation of a task stack view */
+public class TaskStackView extends FrameLayout implements TaskStackCallbacks, TaskViewCallbacks,
+        ViewPoolConsumer<TaskView, Task>, View.OnClickListener {
+    TaskStack mStack;
+    TaskStackViewTouchHandler mTouchHandler;
+    TaskStackViewCallbacks mCb;
+    ViewPool<TaskView, Task> mViewPool;
+
+    // The various rects that define the stack view
+    Rect mRect = new Rect();
+    Rect mStackRect = new Rect();
+    Rect mStackRectSansPeek = new Rect();
+    Rect mTaskRect = new Rect();
+
+    // The virtual stack scroll that we use for the card layout
+    int mStackScroll;
+    int mMinScroll;
+    int mMaxScroll;
+    OverScroller mScroller;
+    ObjectAnimator mScrollAnimator;
+
+    // Optimizations
+    int mHwLayersRefCount;
+    int mStackViewsAnimationDuration;
+    boolean mStackViewsDirty = true;
+    boolean mAwaitingFirstLayout = true;
+
+    public TaskStackView(Context context, TaskStack stack) {
+        super(context);
+        mStack = stack;
+        mStack.setCallbacks(this);
+        mScroller = new OverScroller(context);
+        mTouchHandler = new TaskStackViewTouchHandler(context, this);
+        mViewPool = new ViewPool<TaskView, Task>(context, this);
+    }
+
+    /** Sets the callbacks */
+    void setCallbacks(TaskStackViewCallbacks cb) {
+        mCb = cb;
+    }
+
+    /** Requests that the views be synchronized with the model */
+    void requestSynchronizeStackViewsWithModel() {
+        requestSynchronizeStackViewsWithModel(0);
+    }
+    void requestSynchronizeStackViewsWithModel(int duration) {
+        Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel,
+                "[TaskStackView|requestSynchronize]", "", Console.AnsiYellow);
+        if (!mStackViewsDirty) {
+            invalidate();
+        }
+        if (mAwaitingFirstLayout) {
+            // Skip the animation if we are awaiting first layout
+            mStackViewsAnimationDuration = 0;
+        } else {
+            mStackViewsAnimationDuration = Math.max(mStackViewsAnimationDuration, duration);
+        }
+        mStackViewsDirty = true;
+    }
+
+    // XXX: Optimization: Use a mapping of Task -> View
+    private TaskView getChildViewForTask(Task t) {
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            TaskView tv = (TaskView) getChildAt(i);
+            if (tv.getTask() == t) {
+                return tv;
+            }
+        }
+        return null;
+    }
+
+    /** Update/get the transform */
+    public TaskViewTransform getStackTransform(int indexInStack) {
+        TaskViewTransform transform = new TaskViewTransform();
+
+        // Map the items to an continuous position relative to the current scroll
+        int numPeekCards = Constants.Values.TaskStackView.StackPeekNumCards;
+        float overlapHeight = Constants.Values.TaskStackView.StackOverlapPct * mTaskRect.height();
+        float peekHeight = Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height();
+        float t = ((indexInStack * overlapHeight) - getStackScroll()) / overlapHeight;
+        float boundedT = Math.max(t, -(numPeekCards + 1));
+
+        // Set the scale relative to its position
+        float minScale = Constants.Values.TaskStackView.StackPeekMinScale;
+        float scaleRange = 1f - minScale;
+        float scaleInc = scaleRange / numPeekCards;
+        float scale = Math.max(minScale, Math.min(1f, 1f + (boundedT * scaleInc)));
+        float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
+        transform.scale = scale;
+
+        // Set the translation
+        if (boundedT < 0f) {
+            transform.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
+                    numPeekCards) * peekHeight - scaleYOffset);
+        } else {
+            transform.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
+        }
+
+        // Update the rect and visibility
+        transform.rect.set(mTaskRect);
+        if (t < -(numPeekCards + 1)) {
+            transform.visible = false;
+        } else {
+            transform.rect.offset(0, transform.translationY);
+            Utilities.scaleRectAboutCenter(transform.rect, scale);
+            transform.visible = Rect.intersects(mRect, transform.rect);
+        }
+        transform.t = t;
+        return transform;
+    }
+
+    /** Synchronizes the views with the model */
+    void synchronizeStackViewsWithModel() {
+        Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel,
+                "[TaskStackView|synchronizeViewsWithModel]",
+                "mStackViewsDirty: " + mStackViewsDirty, Console.AnsiYellow);
+        if (mStackViewsDirty) {
+
+            // XXX: Optimization: Use binary search to find the visible range
+            // XXX: Optimize to not call getStackTransform() so many times
+            // XXX: Consider using TaskViewTransform pool to prevent allocations
+            // XXX: Iterate children views, update transforms and remove all that are not visible
+            //      For all remaining tasks, update transforms and if visible add the view
+
+            // Update the visible state of all the tasks
+            ArrayList<Task> tasks = mStack.getTasks();
+            int taskCount = tasks.size();
+            for (int i = 0; i < taskCount; i++) {
+                Task task = tasks.get(i);
+                TaskViewTransform transform = getStackTransform(i);
+                TaskView tv = getChildViewForTask(task);
+
+                if (transform.visible) {
+                    if (tv == null) {
+                        tv = mViewPool.pickUpViewFromPool(task, task);
+                        // When we are picking up a new view from the view pool, prepare it for any
+                        // following animation by putting it in a reasonable place
+                        if (mStackViewsAnimationDuration > 0 && i != 0) {
+                            // XXX: We have to animate when filtering, etc. Maybe we should have a
+                            //      runnable that ensures that tasks are animated in a special way
+                            //      when they are entering the scene?
+                            int fromIndex = (transform.t < 0) ? (i - 1) : (i + 1);
+                            tv.updateViewPropertiesFromTask(null, getStackTransform(fromIndex), 0);
+                        }
+                    }
+                } else {
+                    if (tv != null) {
+                        mViewPool.returnViewToPool(tv);
+                    }
+                }
+            }
+
+            // Update all the current view children
+            // NOTE: We have to iterate in reverse where because we are removing views directly
+            int childCount = getChildCount();
+            for (int i = childCount - 1; i >= 0; i--) {
+                TaskView tv = (TaskView) getChildAt(i);
+                Task task = tv.getTask();
+                TaskViewTransform transform = getStackTransform(mStack.indexOfTask(task));
+                if (!transform.visible) {
+                    mViewPool.returnViewToPool(tv);
+                } else {
+                    tv.updateViewPropertiesFromTask(null, transform, mStackViewsAnimationDuration);
+                }
+            }
+
+            Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel,
+                    "  [TaskStackView|viewChildren]", "" + getChildCount());
+
+            mStackViewsAnimationDuration = 0;
+            mStackViewsDirty = false;
+        }
+    }
+
+    /** Sets the current stack scroll */
+    public void setStackScroll(int value) {
+        mStackScroll = value;
+        requestSynchronizeStackViewsWithModel();
+    }
+
+    /** Gets the current stack scroll */
+    public int getStackScroll() {
+        return mStackScroll;
+    }
+
+    /** Animates the stack scroll into bounds */
+    ObjectAnimator animateBoundScroll(int duration) {
+        int curScroll = getStackScroll();
+        int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
+        if (newScroll != curScroll) {
+            // Enable hw layers on the stack
+            addHwLayersRefCount();
+
+            // Abort any current animations
+            mScroller.abortAnimation();
+            if (mScrollAnimator != null) {
+                mScrollAnimator.cancel();
+                mScrollAnimator.removeAllListeners();
+            }
+
+            // Start a new scroll animation
+            mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll);
+            mScrollAnimator.setDuration(duration);
+            mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    setStackScroll((Integer) animation.getAnimatedValue());
+                }
+            });
+            mScrollAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    // Disable hw layers on the stack
+                    decHwLayersRefCount();
+                }
+            });
+            mScrollAnimator.start();
+        }
+        return mScrollAnimator;
+    }
+
+    /** Aborts any current stack scrolls */
+    void abortBoundScrollAnimation() {
+        if (mScrollAnimator != null) {
+            mScrollAnimator.cancel();
+        }
+    }
+
+    /** Bounds the current scroll if necessary */
+    public boolean boundScroll() {
+        int curScroll = getStackScroll();
+        int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
+        if (newScroll != curScroll) {
+            setStackScroll(newScroll);
+            return true;
+        }
+        return false;
+    }
+
+    /** Returns whether the current scroll is out of bounds */
+    boolean isScrollOutOfBounds() {
+        return (getStackScroll() < 0) || (getStackScroll() > mMaxScroll);
+    }
+
+    /** Updates the min and max virtual scroll bounds */
+    void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
+        // Compute the min and max scroll values
+        int numTasks = Math.max(1, mStack.getTaskCount());
+        int taskHeight = mTaskRect.height();
+        int stackHeight = mStackRectSansPeek.height();
+        int maxScrollHeight = taskHeight + (int) ((numTasks - 1) *
+                Constants.Values.TaskStackView.StackOverlapPct * taskHeight);
+        mMinScroll = Math.min(stackHeight, maxScrollHeight) - stackHeight;
+        mMaxScroll = maxScrollHeight - stackHeight;
+
+        // Debug logging
+        if (Constants.DebugFlags.UI.MeasureAndLayout) {
+            Console.log("  [TaskStack|minScroll] " + mMinScroll);
+            Console.log("  [TaskStack|maxScroll] " + mMaxScroll);
+        }
+
+        if (boundScrollToNewMinMax) {
+            boundScroll();
+        }
+    }
+
+    /** Enables the hw layers and increments the hw layer requirement ref count */
+    void addHwLayersRefCount() {
+        Console.log(Constants.DebugFlags.UI.HwLayers,
+                "[TaskStackView|addHwLayersRefCount] refCount: " +
+                        mHwLayersRefCount + "->" + (mHwLayersRefCount + 1));
+        if (mHwLayersRefCount == 0) {
+            // Enable hw layers on each of the children
+            int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                TaskView tv = (TaskView) getChildAt(i);
+                tv.enableHwLayers();
+            }
+        }
+        mHwLayersRefCount++;
+    }
+
+    /** Decrements the hw layer requirement ref count and disables the hw layers when we don't
+        need them anymore. */
+    void decHwLayersRefCount() {
+        Console.log(Constants.DebugFlags.UI.HwLayers,
+                "[TaskStackView|decHwLayersRefCount] refCount: " +
+                        mHwLayersRefCount + "->" + (mHwLayersRefCount - 1));
+        mHwLayersRefCount--;
+        if (mHwLayersRefCount == 0) {
+            // Disable hw layers on each of the children
+            int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                TaskView tv = (TaskView) getChildAt(i);
+                tv.disableHwLayers();
+            }
+        } else if (mHwLayersRefCount < 0) {
+            throw new RuntimeException("Invalid hw layers ref count");
+        }
+    }
+
+    @Override
+    public void computeScroll() {
+        if (mScroller.computeScrollOffset()) {
+            setStackScroll(mScroller.getCurrY());
+            invalidate();
+
+            // If we just finished scrolling, then disable the hw layers
+            if (mScroller.isFinished()) {
+                decHwLayersRefCount();
+            }
+        }
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        return mTouchHandler.onInterceptTouchEvent(ev);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        return mTouchHandler.onTouchEvent(ev);
+    }
+
+    @Override
+    public void dispatchDraw(Canvas canvas) {
+        Console.log(Constants.DebugFlags.UI.Draw, "[TaskStackView|dispatchDraw]", "",
+                Console.AnsiPurple);
+        synchronizeStackViewsWithModel();
+        super.dispatchDraw(canvas);
+    }
+
+    @Override
+    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        if (Constants.DebugFlags.App.EnableTaskStackClipping) {
+            TaskView tv = (TaskView) child;
+            TaskView nextTv = null;
+            int curIndex = indexOfChild(tv);
+            if (curIndex < (getChildCount() - 1)) {
+                // Clip against the next view (if we aren't animating its alpha)
+                nextTv = (TaskView) getChildAt(curIndex + 1);
+                if (nextTv.getAlpha() == 1f) {
+                    Rect curRect = tv.getClippingRect(Utilities.tmpRect, false);
+                    Rect nextRect = nextTv.getClippingRect(Utilities.tmpRect2, true);
+                    RecentsConfiguration config = RecentsConfiguration.getInstance();
+                    // The hit rects are relative to the task view, which needs to be offset by the
+                    // system bar height
+                    curRect.offset(0, config.systemInsets.top);
+                    nextRect.offset(0, config.systemInsets.top);
+                    // Compute the clip region
+                    Region clipRegion = new Region();
+                    clipRegion.op(curRect, Region.Op.UNION);
+                    clipRegion.op(nextRect, Region.Op.DIFFERENCE);
+                    // Clip the canvas
+                    int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+                    canvas.clipRegion(clipRegion);
+                    boolean invalidate = super.drawChild(canvas, child, drawingTime);
+                    canvas.restoreToCount(saveCount);
+                    return invalidate;
+                }
+            }
+        }
+        return super.drawChild(canvas, child, drawingTime);
+    }
+
+    /** Computes the stack and task rects */
+    public void computeRects(int width, int height) {
+        // Note: We let the stack view be the full height because we want the cards to go under the
+        //       navigation bar if possible.  However, the stack rects which we use to calculate
+        //       max scroll, etc. need to take the nav bar into account
+
+        // Compute the stack rects
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+        mRect.set(0, 0, width, height);
+        mStackRect.set(mRect);
+        mStackRect.bottom -= config.systemInsets.bottom;
+
+        int smallestDimension = Math.min(width, height);
+        int padding = (int) (Constants.Values.TaskStackView.StackPaddingPct * smallestDimension / 2f);
+        mStackRect.inset(padding, padding);
+        mStackRectSansPeek.set(mStackRect);
+        mStackRectSansPeek.top += Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height();
+
+        // Compute the task rect
+        if (RecentsConfiguration.getInstance().layoutVerticalStack) {
+            int minHeight = (int) (mStackRect.height() -
+                    (Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height()));
+            int size = Math.min(minHeight, Math.min(mStackRect.width(), mStackRect.height()));
+            int centerX = mStackRect.centerX();
+            mTaskRect.set(centerX - size / 2, mStackRectSansPeek.top,
+                    centerX + size / 2, mStackRectSansPeek.top + size);
+        } else {
+            int size = Math.min(mStackRect.width(), mStackRect.height());
+            int centerY = mStackRect.centerY();
+            mTaskRect.set(mStackRectSansPeek.top, centerY - size / 2,
+                    mStackRectSansPeek.top + size, centerY + size / 2);
+        }
+
+        // Update the scroll bounds
+        updateMinMaxScroll(false);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        int height = MeasureSpec.getSize(heightMeasureSpec);
+        Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[TaskStackView|measure]",
+                "width: " + width + " height: " + height +
+                " awaitingFirstLayout: " + mAwaitingFirstLayout, Console.AnsiGreen);
+
+        // Compute our stack/task rects
+        computeRects(width, height);
+
+        // Debug logging
+        if (Constants.DebugFlags.UI.MeasureAndLayout) {
+            Console.log("  [TaskStack|fullRect] " + mRect);
+            Console.log("  [TaskStack|stackRect] " + mStackRect);
+            Console.log("  [TaskStack|stackRectSansPeek] " + mStackRectSansPeek);
+            Console.log("  [TaskStack|taskRect] " + mTaskRect);
+        }
+
+        // If this is the first layout, then scroll to the front of the stack and synchronize the
+        // stack views immediately
+        if (mAwaitingFirstLayout) {
+            setStackScroll(mMaxScroll);
+            requestSynchronizeStackViewsWithModel();
+            synchronizeStackViewsWithModel();
+
+            // Animate the icon of the first task view
+            if (Constants.Values.TaskView.AnimateFrontTaskIconOnEnterRecents) {
+                TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
+                if (tv != null) {
+                    tv.animateOnEnterRecents();
+                }
+            }
+        }
+
+        // Measure each of the children
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            TaskView t = (TaskView) getChildAt(i);
+            t.measure(MeasureSpec.makeMeasureSpec(mTaskRect.width(), MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(mTaskRect.height(), MeasureSpec.EXACTLY));
+        }
+
+        setMeasuredDimension(width, height);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[TaskStackView|layout]",
+                "" + new Rect(left, top, right, bottom), Console.AnsiGreen);
+
+        // Debug logging
+        if (Constants.DebugFlags.UI.MeasureAndLayout) {
+            Console.log("  [TaskStack|fullRect] " + mRect);
+            Console.log("  [TaskStack|stackRect] " + mStackRect);
+            Console.log("  [TaskStack|stackRectSansPeek] " + mStackRectSansPeek);
+            Console.log("  [TaskStack|taskRect] " + mTaskRect);
+        }
+
+        // Layout each of the children
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            TaskView t = (TaskView) getChildAt(i);
+            t.layout(mTaskRect.left, mStackRectSansPeek.top,
+                    mTaskRect.right, mStackRectSansPeek.top + mTaskRect.height());
+        }
+
+        if (!mAwaitingFirstLayout) {
+            requestSynchronizeStackViewsWithModel();
+        } else {
+            mAwaitingFirstLayout = false;
+        }
+    }
+
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        requestSynchronizeStackViewsWithModel();
+    }
+
+    public boolean isTransformedTouchPointInView(float x, float y, View child) {
+        return isTransformedTouchPointInView(x, y, child, null);
+    }
+
+    /**** TaskStackCallbacks Implementation ****/
+
+    @Override
+    public void onStackTaskAdded(TaskStack stack, Task t) {
+        requestSynchronizeStackViewsWithModel();
+    }
+
+    @Override
+    public void onStackTaskRemoved(TaskStack stack, Task t) {
+        // Remove the view associated with this task, we can't rely on updateTransforms
+        // to work here because the task is no longer in the list
+        int childCount = getChildCount();
+        for (int i = childCount - 1; i >= 0; i--) {
+            TaskView tv = (TaskView) getChildAt(i);
+            if (tv.getTask() == t) {
+                mViewPool.returnViewToPool(tv);
+                break;
+            }
+        }
+
+        updateMinMaxScroll(true);
+        requestSynchronizeStackViewsWithModel(Constants.Values.TaskStackView.Animation.TaskRemovedReshuffleDuration);
+    }
+
+    @Override
+    public void onStackFiltered(TaskStack stack) {
+        requestSynchronizeStackViewsWithModel();
+    }
+
+    @Override
+    public void onStackUnfiltered(TaskStack stack) {
+        requestSynchronizeStackViewsWithModel();
+    }
+
+    /**** ViewPoolConsumer Implementation ****/
+
+    @Override
+    public TaskView createView(Context context) {
+        Console.log(Constants.DebugFlags.ViewPool.PoolCallbacks, "[TaskStackView|createPoolView]");
+        return new TaskView(context);
+    }
+
+    @Override
+    public void prepareViewToEnterPool(TaskView tv) {
+        Task task = tv.getTask();
+        tv.resetViewProperties();
+        Console.log(Constants.DebugFlags.ViewPool.PoolCallbacks, "[TaskStackView|returnToPool]",
+                tv.getTask() + " tv: " + tv);
+
+        // Report that this tasks's data is no longer being used
+        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+        loader.unloadTaskData(task);
+        tv.unbindFromTask();
+
+        // Detach the view from the hierarchy
+        detachViewFromParent(tv);
+
+        // Disable hw layers on this view
+        tv.disableHwLayers();
+    }
+
+    @Override
+    public void prepareViewToLeavePool(TaskView tv, Task prepareData, boolean isNewView) {
+        Console.log(Constants.DebugFlags.ViewPool.PoolCallbacks, "[TaskStackView|leavePool]",
+                "isNewView: " + isNewView);
+
+        // Setup and attach the view to the window
+        Task task = prepareData;
+        // We try and rebind the task (this MUST be done before the task filled)
+        tv.bindToTask(task, this);
+        // Request that this tasks's data be filled
+        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+        loader.loadTaskData(task);
+        tv.syncToTask();
+
+        // Find the index where this task should be placed in the children
+        int insertIndex = -1;
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            Task tvTask = ((TaskView) getChildAt(i)).getTask();
+            if (mStack.containsTask(task) && (mStack.indexOfTask(task) < mStack.indexOfTask(tvTask))) {
+                insertIndex = i;
+                break;
+            }
+        }
+
+        // Add/attach the view to the hierarchy
+        Console.log(Constants.DebugFlags.ViewPool.PoolCallbacks, "  [TaskStackView|insertIndex]",
+                "" + insertIndex);
+        if (isNewView) {
+            addView(tv, insertIndex);
+            tv.setOnClickListener(this);
+        } else {
+            attachViewToParent(tv, insertIndex, tv.getLayoutParams());
+        }
+
+        // Enable hw layers on this view if hw layers are enabled on the stack
+        if (mHwLayersRefCount > 0) {
+            tv.enableHwLayers();
+        }
+    }
+
+    @Override
+    public boolean hasPreferredData(TaskView tv, Task preferredData) {
+        return (tv.getTask() == preferredData);
+    }
+
+    /**** TaskViewCallbacks Implementation ****/
+
+    @Override
+    public void onTaskIconClicked(TaskView tv) {
+        Console.log(Constants.DebugFlags.UI.ClickEvents, "[TaskStack|Clicked|Icon]",
+                tv.getTask() + " is currently filtered: " + mStack.hasFilteredTasks(),
+                Console.AnsiCyan);
+        if (Constants.DebugFlags.App.EnableTaskFiltering) {
+            if (mStack.hasFilteredTasks()) {
+                mStack.unfilterTasks();
+            } else {
+                mStack.filterTasks(tv.getTask());
+            }
+        } else {
+            Toast.makeText(getContext(), "Task Filtering TBD", Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    /**** View.OnClickListener Implementation ****/
+
+    @Override
+    public void onClick(View v) {
+        TaskView tv = (TaskView) v;
+        Task task = tv.getTask();
+        Console.log(Constants.DebugFlags.UI.ClickEvents, "[TaskStack|Clicked|Thumbnail]",
+                task + " cb: " + mCb);
+
+        if (mCb != null) {
+            mCb.onTaskLaunched(this, tv, mStack, task);
+        }
+    }
+}
+
+/* Handles touch events */
+class TaskStackViewTouchHandler {
+    static int INACTIVE_POINTER_ID = -1;
+
+    TaskStackView mSv;
+    VelocityTracker mVelocityTracker;
+
+    boolean mIsScrolling;
+    boolean mIsSwiping;
+
+    int mInitialMotionX, mInitialMotionY;
+    int mLastMotionX, mLastMotionY;
+    int mActivePointerId = INACTIVE_POINTER_ID;
+    TaskView mActiveTaskView = null;
+
+    int mTotalScrollMotion;
+    int mMinimumVelocity;
+    int mMaximumVelocity;
+    // The scroll touch slop is used to calculate when we start scrolling
+    int mScrollTouchSlop;
+    // The swipe touch slop is used to calculate when we start swiping left/right, this takes
+    // precendence over the scroll touch slop in case the user makes a gesture that starts scrolling
+    // but is intended to be a swipe
+    int mSwipeTouchSlop;
+    // After a certain amount of scrolling, we should start ignoring checks for swiping
+    int mMaxScrollMotionToRejectSwipe;
+
+    public TaskStackViewTouchHandler(Context context, TaskStackView sv) {
+        ViewConfiguration configuration = ViewConfiguration.get(context);
+        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
+        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+        mScrollTouchSlop = configuration.getScaledTouchSlop();
+        mSwipeTouchSlop = 2 * mScrollTouchSlop;
+        mMaxScrollMotionToRejectSwipe = 4 * mScrollTouchSlop;
+        mSv = sv;
+    }
+
+    /** Velocity tracker helpers */
+    void initOrResetVelocityTracker() {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        } else {
+            mVelocityTracker.clear();
+        }
+    }
+    void initVelocityTrackerIfNotExists() {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+    }
+    void recycleVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+    }
+
+    /** Returns the view at the specified coordinates */
+    TaskView findViewAtPoint(int x, int y) {
+        int childCount = mSv.getChildCount();
+        for (int i = childCount - 1; i >= 0; i--) {
+            TaskView tv = (TaskView) mSv.getChildAt(i);
+            if (tv.getVisibility() == View.VISIBLE) {
+                if (mSv.isTransformedTouchPointInView(x, y, tv)) {
+                    return tv;
+                }
+            }
+        }
+        return null;
+    }
+
+    /** Touch preprocessing for handling below */
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        Console.log(Constants.DebugFlags.UI.TouchEvents,
+                "[TaskStackViewTouchHandler|interceptTouchEvent]",
+                Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
+
+        boolean hasChildren = (mSv.getChildCount() > 0);
+        if (!hasChildren) {
+            return false;
+        }
+
+        boolean wasScrolling = !mSv.mScroller.isFinished() ||
+                (mSv.mScrollAnimator != null && mSv.mScrollAnimator.isRunning());
+        int action = ev.getAction();
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN: {
+                // Save the touch down info
+                mInitialMotionX = mLastMotionX = (int) ev.getX();
+                mInitialMotionY = mLastMotionY = (int) ev.getY();
+                mActivePointerId = ev.getPointerId(0);
+                mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
+                // Stop the current scroll if it is still flinging
+                mSv.mScroller.abortAnimation();
+                mSv.abortBoundScrollAnimation();
+                // Initialize the velocity tracker
+                initOrResetVelocityTracker();
+                mVelocityTracker.addMovement(ev);
+                // Check if the scroller is finished yet
+                mIsScrolling = !mSv.mScroller.isFinished();
+                mIsSwiping = false;
+                break;
+            }
+            case MotionEvent.ACTION_MOVE: {
+                if (mActivePointerId == INACTIVE_POINTER_ID) break;
+
+                int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+                int y = (int) ev.getY(activePointerIndex);
+                int x = (int) ev.getX(activePointerIndex);
+                if (mActiveTaskView != null &&
+                        mTotalScrollMotion < mMaxScrollMotionToRejectSwipe &&
+                        Math.abs(x - mInitialMotionX) > Math.abs(y - mInitialMotionY) &&
+                        Math.abs(x - mInitialMotionX) > mSwipeTouchSlop) {
+                    // Start swiping and stop scrolling
+                    mIsScrolling = false;
+                    mIsSwiping = true;
+                    System.out.println("SWIPING: " + mActiveTaskView);
+                    // Initialize the velocity tracker if necessary
+                    initOrResetVelocityTracker();
+                    mVelocityTracker.addMovement(ev);
+                    // Disallow parents from intercepting touch events
+                    final ViewParent parent = mSv.getParent();
+                    if (parent != null) {
+                        parent.requestDisallowInterceptTouchEvent(true);
+                    }
+                    // Enable HW layers
+                    mSv.addHwLayersRefCount();
+                } else if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
+                    // Save the touch move info
+                    mIsScrolling = true;
+                    // Initialize the velocity tracker if necessary
+                    initVelocityTrackerIfNotExists();
+                    mVelocityTracker.addMovement(ev);
+                    // Disallow parents from intercepting touch events
+                    final ViewParent parent = mSv.getParent();
+                    if (parent != null) {
+                        parent.requestDisallowInterceptTouchEvent(true);
+                    }
+                    // Enable HW layers
+                    mSv.addHwLayersRefCount();
+                }
+
+                mLastMotionX = x;
+                mLastMotionY = y;
+                break;
+            }
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP: {
+                // Animate the scroll back if we've cancelled
+                mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration);
+                // Reset the drag state and the velocity tracker
+                mIsScrolling = false;
+                mIsSwiping = false;
+                mActivePointerId = INACTIVE_POINTER_ID;
+                mActiveTaskView = null;
+                mTotalScrollMotion = 0;
+                recycleVelocityTracker();
+                break;
+            }
+        }
+
+        return wasScrolling || mIsScrolling || mIsSwiping;
+    }
+
+    /** Handles touch events once we have intercepted them */
+    public boolean onTouchEvent(MotionEvent ev) {
+        Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel,
+                "[TaskStackViewTouchHandler|touchEvent]",
+                Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
+
+        // Short circuit if we have no children
+        boolean hasChildren = (mSv.getChildCount() > 0);
+        if (!hasChildren) {
+            return false;
+        }
+
+        // Update the velocity tracker
+        initVelocityTrackerIfNotExists();
+        mVelocityTracker.addMovement(ev);
+
+        int action = ev.getAction();
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN: {
+                // Save the touch down info
+                mInitialMotionX = mLastMotionX = (int) ev.getX();
+                mInitialMotionY = mLastMotionY = (int) ev.getY();
+                mActivePointerId = ev.getPointerId(0);
+                mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
+                // Stop the current scroll if it is still flinging
+                mSv.mScroller.abortAnimation();
+                mSv.abortBoundScrollAnimation();
+                // Initialize the velocity tracker
+                initOrResetVelocityTracker();
+                mVelocityTracker.addMovement(ev);
+                // XXX: Set mIsScrolling or mIsSwiping?
+                // Disallow parents from intercepting touch events
+                final ViewParent parent = mSv.getParent();
+                if (parent != null) {
+                    parent.requestDisallowInterceptTouchEvent(true);
+                }
+                break;
+            }
+            case MotionEvent.ACTION_MOVE: {
+                if (mActivePointerId == INACTIVE_POINTER_ID) break;
+
+                int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+                int x = (int) ev.getX(activePointerIndex);
+                int y = (int) ev.getY(activePointerIndex);
+                int deltaY = mLastMotionY - y;
+                int deltaX = x - mLastMotionX;
+                if (!mIsSwiping) {
+                    if (mActiveTaskView != null &&
+                            mTotalScrollMotion < mMaxScrollMotionToRejectSwipe &&
+                            Math.abs(x - mInitialMotionX) > Math.abs(y - mInitialMotionY) &&
+                            Math.abs(x - mInitialMotionX) > mSwipeTouchSlop) {
+                        mIsScrolling = false;
+                        mIsSwiping = true;
+                        System.out.println("SWIPING: " + mActiveTaskView);
+                        // Initialize the velocity tracker if necessary
+                        initOrResetVelocityTracker();
+                        mVelocityTracker.addMovement(ev);
+                        // Disallow parents from intercepting touch events
+                        final ViewParent parent = mSv.getParent();
+                        if (parent != null) {
+                            parent.requestDisallowInterceptTouchEvent(true);
+                        }
+                        // Enable HW layers
+                        mSv.addHwLayersRefCount();
+                    }
+                }
+                if (!mIsSwiping && !mIsScrolling) {
+                    if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
+                        mIsScrolling = true;
+                        // Initialize the velocity tracker
+                        initOrResetVelocityTracker();
+                        mVelocityTracker.addMovement(ev);
+                        // Disallow parents from intercepting touch events
+                        final ViewParent parent = mSv.getParent();
+                        if (parent != null) {
+                            parent.requestDisallowInterceptTouchEvent(true);
+                        }
+                        // Enable HW layers
+                        mSv.addHwLayersRefCount();
+                    }
+                }
+                if (mIsScrolling) {
+                    mSv.setStackScroll(mSv.getStackScroll() + deltaY);
+                    if (mSv.isScrollOutOfBounds()) {
+                        mVelocityTracker.clear();
+                    }
+                } else if (mIsSwiping) {
+                    mActiveTaskView.setTranslationX(mActiveTaskView.getTranslationX() + deltaX);
+                }
+                mLastMotionX = x;
+                mLastMotionY = y;
+                mTotalScrollMotion += Math.abs(deltaY);
+                break;
+            }
+            case MotionEvent.ACTION_UP: {
+                if (mIsScrolling || mIsSwiping) {
+                    final TaskView activeTv = mActiveTaskView;
+                    final VelocityTracker velocityTracker = mVelocityTracker;
+                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+
+                    if (mIsSwiping) {
+                        int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
+                        if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+                            // Fling to dismiss
+                            int newScrollX = (int) (Math.signum(initialVelocity) *
+                                    activeTv.getMeasuredWidth());
+                            int duration = Math.min(Constants.Values.TaskStackView.Animation.SwipeDismissDuration,
+                                    (int) (Math.abs(newScrollX - activeTv.getScrollX()) *
+                                            1000f / Math.abs(initialVelocity)));
+                            activeTv.animate()
+                                    .translationX(newScrollX)
+                                    .alpha(0f)
+                                    .setDuration(duration)
+                                    .setListener(new AnimatorListenerAdapter() {
+                                        @Override
+                                        public void onAnimationEnd(Animator animation) {
+                                            Task task = activeTv.getTask();
+                                            Activity activity = (Activity) mSv.getContext();
+
+                                            // We have to disable the listener to ensure that we
+                                            // don't hit this again
+                                            activeTv.animate().setListener(null);
+
+                                            // Remove the task from the view
+                                            mSv.mStack.removeTask(task);
+
+                                            // Remove any stored data from the loader
+                                            RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+                                            loader.deleteTaskData(task);
+
+                                            // Remove the task from activity manager
+                                            final ActivityManager am = (ActivityManager)
+                                                activity.getSystemService(Context.ACTIVITY_SERVICE);
+                                            if (am != null) {
+                                                am.removeTask(activeTv.getTask().id,
+                                                        ActivityManager.REMOVE_TASK_KILL_PROCESS);
+                                            }
+
+                                            // If there are no remaining tasks, then just close the activity
+                                            if (mSv.mStack.getTaskCount() == 0) {
+                                                activity.finish();
+                                            }
+
+                                            // Disable HW layers
+                                            mSv.decHwLayersRefCount();
+                                        }
+                                    })
+                                    .start();
+                            // Enable HW layers
+                            mSv.addHwLayersRefCount();
+                        } else {
+                            // Animate it back into place
+                            // XXX: Make this animation a function of the velocity OR distance
+                            int duration = Constants.Values.TaskStackView.Animation.SwipeSnapBackDuration;
+                            activeTv.animate()
+                                    .translationX(0)
+                                    .setDuration(duration)
+                                    .setListener(new AnimatorListenerAdapter() {
+                                        @Override
+                                        public void onAnimationEnd(Animator animation) {
+                                            // Disable HW layers
+                                            mSv.decHwLayersRefCount();
+                                        }
+                                    })
+                                    .start();
+                            // Enable HW layers
+                            mSv.addHwLayersRefCount();
+                        }
+                    } else {
+                        int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
+                        if ((Math.abs(velocity) > mMinimumVelocity)) {
+                            Console.log(Constants.DebugFlags.UI.TouchEvents,
+                                "[TaskStackViewTouchHandler|fling]",
+                                "scroll: " + mSv.getStackScroll() + " velocity: " + velocity,
+                                    Console.AnsiGreen);
+                            // Enable HW layers on the stack
+                            mSv.addHwLayersRefCount();
+                            // Fling scroll
+                            mSv.mScroller.fling(0, mSv.getStackScroll(),
+                                    0, -velocity,
+                                    0, 0,
+                                    mSv.mMinScroll, mSv.mMaxScroll,
+                                    0, 0);
+                            // Invalidate to kick off computeScroll
+                            mSv.invalidate();
+                        } else if (mSv.isScrollOutOfBounds()) {
+                            // Animate the scroll back into bounds
+                            // XXX: Make this animation a function of the velocity OR distance
+                            mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration);
+                        }
+                    }
+                }
+
+                mActivePointerId = INACTIVE_POINTER_ID;
+                mIsScrolling = false;
+                mIsSwiping = false;
+                mTotalScrollMotion = 0;
+                recycleVelocityTracker();
+                // Disable HW layers
+                mSv.decHwLayersRefCount();
+                break;
+            }
+            case MotionEvent.ACTION_CANCEL: {
+                if (mIsScrolling || mIsSwiping) {
+                    if (mIsSwiping) {
+                        // Animate it back into place
+                        // XXX: Make this animation a function of the velocity OR distance
+                        int duration = Constants.Values.TaskStackView.Animation.SwipeSnapBackDuration;
+                        mActiveTaskView.animate()
+                                .translationX(0)
+                                .setDuration(duration)
+                                .start();
+                    } else {
+                        // Animate the scroll back into bounds
+                        // XXX: Make this animation a function of the velocity OR distance
+                        mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration);
+                    }
+                }
+
+                mActivePointerId = INACTIVE_POINTER_ID;
+                mIsScrolling = false;
+                mIsSwiping = false;
+                mTotalScrollMotion = 0;
+                recycleVelocityTracker();
+                // Disable HW layers
+                mSv.decHwLayersRefCount();
+                break;
+            }
+        }
+        return true;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
new file mode 100644
index 0000000..b1d0d13
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -0,0 +1,386 @@
+/*
+ * 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.recents.views;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.view.Gravity;
+import android.view.View;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import com.android.systemui.recents.Console;
+import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskCallbacks;
+
+/** The TaskView callbacks */
+interface TaskViewCallbacks {
+    public void onTaskIconClicked(TaskView tv);
+    // public void onTaskViewReboundToTask(TaskView tv, Task t);
+}
+
+/** The task thumbnail view */
+class TaskThumbnailView extends ImageView {
+    Task mTask;
+    int mBarColor;
+
+    Path mRoundedRectClipPath = new Path();
+
+    public TaskThumbnailView(Context context) {
+        super(context);
+        setScaleType(ScaleType.FIT_XY);
+    }
+
+    /** Binds the thumbnail view to the task */
+    void rebindToTask(Task t, boolean animate) {
+        mTask = t;
+        if (t.thumbnail != null) {
+            // Update the bar color
+            if (Constants.Values.TaskView.DrawColoredTaskBars) {
+                int[] colors = {0xFFCC0C39, 0xFFE6781E, 0xFFC8CF02, 0xFF1693A7};
+                mBarColor = colors[mTask.intent.getComponent().getPackageName().length() % colors.length];
+            }
+
+            setImageBitmap(t.thumbnail);
+            if (animate) {
+                setAlpha(0f);
+                animate().alpha(1f)
+                        .setDuration(Constants.Values.TaskView.Animation.TaskDataUpdatedFadeDuration)
+                        .start();
+            }
+        }
+    }
+
+    /** Unbinds the thumbnail view from the task */
+    void unbindFromTask() {
+        mTask = null;
+        setImageDrawable(null);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        // Update the rounded rect clip path
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+        float radius = config.pxFromDp(Constants.Values.TaskView.RoundedCornerRadiusDps);
+        mRoundedRectClipPath.reset();
+        mRoundedRectClipPath.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()),
+                radius, radius, Path.Direction.CW);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (Constants.Values.TaskView.UseRoundedCorners) {
+            canvas.clipPath(mRoundedRectClipPath);
+        }
+
+        super.onDraw(canvas);
+
+        if (Constants.Values.TaskView.DrawColoredTaskBars) {
+            RecentsConfiguration config = RecentsConfiguration.getInstance();
+            int taskBarHeight = config.pxFromDp(Constants.Values.TaskView.TaskBarHeightDps);
+            // XXX: If we actually use this, this should be pulled out into a TextView that we
+            // inflate
+
+            // Draw the task bar
+            Rect r = new Rect();
+            Paint p = new Paint();
+            p.setAntiAlias(true);
+            p.setSubpixelText(true);
+            p.setColor(mBarColor);
+            p.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
+            canvas.drawRect(0, 0, getMeasuredWidth(), taskBarHeight, p);
+            p.setColor(0xFFffffff);
+            p.setTextSize(68);
+            p.getTextBounds("X", 0, 1, r);
+            int offset = (int) (taskBarHeight - r.height()) / 2;
+            canvas.drawText(mTask.title, offset, offset + r.height(), p);
+        }
+    }
+}
+
+/* The task icon view */
+class TaskIconView extends ImageView {
+    Task mTask;
+
+    Path mClipPath = new Path();
+    float mClipRadius;
+    Point mClipOrigin = new Point();
+    ObjectAnimator mCircularClipAnimator;
+
+    public TaskIconView(Context context) {
+        super(context);
+        mClipPath = new Path();
+        mClipRadius = 1f;
+    }
+
+    /** Binds the icon view to the task */
+    void rebindToTask(Task t, boolean animate) {
+        mTask = t;
+        if (t.icon != null) {
+            setImageDrawable(t.icon);
+            if (animate) {
+                setAlpha(0f);
+                animate().alpha(1f)
+                        .setDuration(Constants.Values.TaskView.Animation.TaskDataUpdatedFadeDuration)
+                        .start();
+            }
+        }
+    }
+
+    /** Unbinds the icon view from the task */
+    void unbindFromTask() {
+        mTask = null;
+        setImageDrawable(null);
+    }
+
+    /** Sets the circular clip radius on the icon */
+    public void setCircularClipRadius(float r) {
+        Console.log(Constants.DebugFlags.UI.Clipping, "[TaskView|setCircularClip]", "" + r);
+        mClipRadius = r;
+        invalidate();
+    }
+
+    /** Gets the circular clip radius on the icon */
+    public float getCircularClipRadius() {
+        return mClipRadius;
+    }
+
+    /** Animates the circular clip radius on the icon */
+    void animateCircularClip(boolean brNotTl, float newRadius, int duration, int startDelay,
+                             TimeInterpolator interpolator,
+                             AnimatorListenerAdapter listener) {
+        if (mCircularClipAnimator != null) {
+            mCircularClipAnimator.cancel();
+            mCircularClipAnimator.removeAllListeners();
+        }
+        if (brNotTl) {
+            mClipOrigin.set(0, 0);
+        } else {
+            mClipOrigin.set(getMeasuredWidth(), getMeasuredHeight());
+        }
+        mCircularClipAnimator = ObjectAnimator.ofFloat(this, "circularClipRadius", newRadius);
+        mCircularClipAnimator.setStartDelay(startDelay);
+        mCircularClipAnimator.setDuration(duration);
+        mCircularClipAnimator.setInterpolator(interpolator);
+        if (listener != null) {
+            mCircularClipAnimator.addListener(listener);
+        }
+        mCircularClipAnimator.start();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+        int width = getMeasuredWidth();
+        int height = getMeasuredHeight();
+        int maxSize = (int) Math.ceil(Math.sqrt(width * width + height * height));
+        mClipPath.reset();
+        mClipPath.addCircle(mClipOrigin.x, mClipOrigin.y, mClipRadius * maxSize, Path.Direction.CW);
+        canvas.clipPath(mClipPath);
+        super.onDraw(canvas);
+        canvas.restoreToCount(saveCount);
+    }
+}
+
+/* A task view */
+public class TaskView extends FrameLayout implements View.OnClickListener, TaskCallbacks {
+    Task mTask;
+    TaskThumbnailView mThumbnailView;
+    TaskIconView mIconView;
+    TaskViewCallbacks mCb;
+
+    public TaskView(Context context) {
+        super(context);
+        mThumbnailView = new TaskThumbnailView(context);
+        mIconView = new TaskIconView(context);
+        mIconView.setOnClickListener(this);
+        addView(mThumbnailView);
+        addView(mIconView);
+
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+        int barHeight = config.pxFromDp(Constants.Values.TaskView.TaskBarHeightDps);
+        int iconSize = config.pxFromDp(Constants.Values.TaskView.TaskIconSizeDps);
+        int offset = barHeight - (iconSize / 2);
+
+        // XXX: Lets keep the icon in the corner for the time being
+        offset = iconSize / 4;
+
+        /*
+        ((LayoutParams) mThumbnailView.getLayoutParams()).leftMargin = barHeight / 2;
+        ((LayoutParams) mThumbnailView.getLayoutParams()).rightMargin = barHeight / 2;
+        ((LayoutParams) mThumbnailView.getLayoutParams()).bottomMargin = barHeight;
+        */
+        ((LayoutParams) mIconView.getLayoutParams()).gravity = Gravity.END;
+        ((LayoutParams) mIconView.getLayoutParams()).width = iconSize;
+        ((LayoutParams) mIconView.getLayoutParams()).height = iconSize;
+        ((LayoutParams) mIconView.getLayoutParams()).topMargin = offset;
+        ((LayoutParams) mIconView.getLayoutParams()).rightMargin = offset;
+    }
+
+    /** Set the task and callback */
+    void bindToTask(Task t, TaskViewCallbacks cb) {
+        mTask = t;
+        mTask.setCallbacks(this);
+        mCb = cb;
+    }
+
+    /** Actually synchronizes the model data into the views */
+    void syncToTask() {
+        mThumbnailView.rebindToTask(mTask, false);
+        mIconView.rebindToTask(mTask, false);
+    }
+
+    /** Unset the task and callback */
+    void unbindFromTask() {
+        mTask.setCallbacks(null);
+        mThumbnailView.unbindFromTask();
+        mIconView.unbindFromTask();
+    }
+
+    /** Gets the task */
+    Task getTask() {
+        return mTask;
+    }
+
+    /** Synchronizes this view's properties with the task's transform */
+    void updateViewPropertiesFromTask(TaskViewTransform animateFromTransform,
+                                      TaskViewTransform transform, int duration) {
+        if (duration > 0) {
+            if (animateFromTransform != null) {
+                setTranslationY(animateFromTransform.translationY);
+                setScaleX(animateFromTransform.scale);
+                setScaleY(animateFromTransform.scale);
+            }
+            animate().translationY(transform.translationY)
+                    .scaleX(transform.scale)
+                    .scaleY(transform.scale)
+                    .setDuration(duration)
+                    .setInterpolator(new AccelerateDecelerateInterpolator())
+                    .start();
+        } else {
+            setTranslationY(transform.translationY);
+            setScaleX(transform.scale);
+            setScaleY(transform.scale);
+        }
+    }
+
+    /** Resets this view's properties */
+    void resetViewProperties() {
+        setTranslationX(0f);
+        setTranslationY(0f);
+        setScaleX(1f);
+        setScaleY(1f);
+        setAlpha(1f);
+    }
+
+    /** Animates this task view as it enters recents */
+    public void animateOnEnterRecents() {
+        mIconView.setCircularClipRadius(0f);
+        mIconView.animateCircularClip(true, 1f,
+            Constants.Values.TaskView.Animation.TaskIconCircularClipInDuration,
+            300, new AccelerateInterpolator(), null);
+    }
+
+    /** Animates this task view as it exits recents */
+    public void animateOnLeavingRecents(final Runnable r) {
+        if (Constants.Values.TaskView.AnimateFrontTaskIconOnLeavingUseClip) {
+            mIconView.animateCircularClip(false, 0f,
+                Constants.Values.TaskView.Animation.TaskIconCircularClipOutDuration, 0,
+                new DecelerateInterpolator(),
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        r.run();
+                    }
+                });
+        } else {
+            mIconView.animate()
+                .alpha(0f)
+                .setDuration(Constants.Values.TaskView.Animation.TaskIconCircularClipOutDuration)
+                .setInterpolator(new DecelerateInterpolator())
+                .setListener(
+                    new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            r.run();
+                        }
+                    })
+                .start();
+        }
+    }
+
+    /** Returns the rect we want to clip (it may not be the full rect) */
+    Rect getClippingRect(Rect outRect, boolean accountForRoundedRects) {
+        getHitRect(outRect);
+        // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
+        outRect.right = outRect.left + mThumbnailView.getRight();
+        outRect.bottom = outRect.top + mThumbnailView.getBottom();
+        // We need to shrink the next rect by the rounded corners since those are draw on
+        // top of the current view
+        if (accountForRoundedRects) {
+            RecentsConfiguration config = RecentsConfiguration.getInstance();
+            float radius = config.pxFromDp(Constants.Values.TaskView.RoundedCornerRadiusDps);
+            outRect.inset((int) radius, (int) radius);
+        }
+        return outRect;
+    }
+
+    /** Enable the hw layers on this task view */
+    void enableHwLayers() {
+        Console.log(Constants.DebugFlags.UI.HwLayers, "[TaskView|enableHwLayers]");
+        mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+    }
+
+    /** Disable the hw layers on this task view */
+    void disableHwLayers() {
+        Console.log(Constants.DebugFlags.UI.HwLayers, "[TaskView|disableHwLayers]");
+        mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
+    }
+
+    @Override
+    public void onTaskDataChanged(Task task) {
+        Console.log(Constants.DebugFlags.App.EnableBackgroundTaskLoading,
+                "[TaskView|onTaskDataChanged]", task);
+
+        // Only update this task view if the changed task is the same as the task for this view
+        if (mTask == task) {
+            mThumbnailView.rebindToTask(mTask, true);
+            mIconView.rebindToTask(mTask, true);
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        mCb.onTaskIconClicked(this);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
new file mode 100644
index 0000000..66c52a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -0,0 +1,35 @@
+/*
+ * 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.recents.views;
+
+import android.graphics.Rect;
+
+
+/* The transform state for a task view */
+public class TaskViewTransform {
+    public int translationY = 0;
+    public float scale = 1f;
+    public boolean visible = true;
+    public Rect rect = new Rect();
+    float t;
+
+    @Override
+    public String toString() {
+        return "TaskViewTransform y: " + translationY + " scale: " + scale +
+                " visible: " + visible + " rect: " + rect;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java
new file mode 100644
index 0000000..f7d7095
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java
@@ -0,0 +1,69 @@
+/*
+ * 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.recents.views;
+
+import android.content.Context;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+
+/* A view pool to manage more views than we can visibly handle */
+public class ViewPool<V, T> {
+    Context mContext;
+    ViewPoolConsumer<V, T> mViewCreator;
+    LinkedList<V> mPool = new LinkedList<V>();
+
+    /** Initializes the pool with a fixed predetermined pool size */
+    public ViewPool(Context context, ViewPoolConsumer<V, T> viewCreator) {
+        mContext = context;
+        mViewCreator = viewCreator;
+    }
+
+    /** Returns a view into the pool */
+    void returnViewToPool(V v) {
+        mViewCreator.prepareViewToEnterPool(v);
+        mPool.push(v);
+    }
+
+    /** Gets a view from the pool and prepares it */
+    V pickUpViewFromPool(T preferredData, T prepareData) {
+        V v = null;
+        boolean isNewView = false;
+        if (mPool.isEmpty()) {
+            v = mViewCreator.createView(mContext);
+            isNewView = true;
+        } else {
+            // Try and find a preferred view
+            Iterator<V> iter = mPool.iterator();
+            while (iter.hasNext()) {
+                V vpv = iter.next();
+                if (mViewCreator.hasPreferredData(vpv, preferredData)) {
+                    v = vpv;
+                    iter.remove();
+                    break;
+                }
+            }
+            // Otherwise, just grab the first view
+            if (v == null) {
+                v = mPool.pop();
+            }
+        }
+        mViewCreator.prepareViewToLeavePool(v, prepareData, isNewView);
+        return v;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewPoolConsumer.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewPoolConsumer.java
new file mode 100644
index 0000000..50f45bf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewPoolConsumer.java
@@ -0,0 +1,28 @@
+/*
+ * 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.recents.views;
+
+import android.content.Context;
+
+
+/* An interface to the consumer of a view pool */
+public interface ViewPoolConsumer<V, T> {
+    public V createView(Context context);
+    public void prepareViewToEnterPool(V v);
+    public void prepareViewToLeavePool(V v, T prepareData, boolean isNewView);
+    public boolean hasPreferredData(V v, T preferredData);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index eb07d88..a89921f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -931,6 +931,7 @@
         }
 
         if (contentViewLocal != null) {
+            contentViewLocal.setIsRootNamespace(true);
             SizeAdaptiveLayout.LayoutParams params =
                     new SizeAdaptiveLayout.LayoutParams(contentViewLocal.getLayoutParams());
             params.minHeight = minHeight;
@@ -938,6 +939,7 @@
             expanded.addView(contentViewLocal, params);
         }
         if (bigContentViewLocal != null) {
+            bigContentViewLocal.setIsRootNamespace(true);
             SizeAdaptiveLayout.LayoutParams params =
                     new SizeAdaptiveLayout.LayoutParams(bigContentViewLocal.getLayoutParams());
             params.minHeight = minHeight+1;
@@ -955,6 +957,7 @@
                         expandedPublic, mOnClickHandler);
 
                 if (publicViewLocal != null) {
+                    publicViewLocal.setIsRootNamespace(true);
                     SizeAdaptiveLayout.LayoutParams params =
                             new SizeAdaptiveLayout.LayoutParams(publicViewLocal.getLayoutParams());
                     params.minHeight = minHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java
index c4d2cce..fa7f96a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java
@@ -28,6 +28,8 @@
 import android.graphics.Typeface;
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.shapes.PathShape;
+import android.os.AsyncTask;
+import android.os.Vibrator;
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
 import android.text.TextPaint;
@@ -45,7 +47,6 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListView;
-import android.widget.PopupWindow;
 import android.widget.RelativeLayout;
 import android.widget.Spinner;
 import android.widget.TextView;
@@ -79,6 +80,7 @@
     private final Rect mLayoutRect = new Rect();
     private final UntilPager mUntilPager;
     private final AlarmWarning mAlarmWarning;
+    private final int mPopDuration;
 
     private float mDownY;
     private int mDownBottom;
@@ -87,6 +89,7 @@
     private int mBottom;
     private int mWidthSpec;
     private Adapter mAdapter;
+    private boolean mPopped;
 
     public ZenModeView(Context context) {
         this(context, null);
@@ -144,7 +147,6 @@
         lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
         addView(mModeSpinner, lp);
 
-
         mUntilPager = new UntilPager(mContext, mPathPaint, iconSize);
         mUntilPager.setId(android.R.id.tabhost);
         mUntilPager.setAlpha(0);
@@ -165,6 +167,8 @@
         mHintText.setGravity(Gravity.CENTER);
         mHintText.setTextColor(GRAY);
         addView(mHintText, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+
+        mPopDuration = mContext.getResources().getInteger(R.integer.blinds_pop_duration_ms);
     }
 
     private boolean isApplicable() {
@@ -180,9 +184,11 @@
             public void onAnimationUpdate(ValueAnimator animation) {
                 final float f = animation.getAnimatedFraction();
                 final int hintBottom = mHintText.getBottom();
-                setPeeked(hintBottom + (int)((1-f) * (startBottom - hintBottom)), max);
-                if (f == 1) {
+                final boolean isDone = f == 1;
+                setPeeked(hintBottom + (int)((1-f) * (startBottom - hintBottom)), max, isDone);
+                if (isDone) {
                     mPeekable = true;
+                    mPopped = false;
                     mClosing = false;
                     mModeSpinner.updateState();
                     if (mAdapter != null) {
@@ -335,11 +341,15 @@
             return true;
         } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
             final float dy = event.getY() - mDownY;
-            setPeeked(mDownBottom + (int)dy, getExpandedBottom());
+            if (!mPopped) {
+                mPopped = true;
+                AsyncTask.execute(mPopVibration);
+            }
+            setPeeked(mDownBottom + (int)dy, getExpandedBottom(), false);
         } else if (event.getAction() == MotionEvent.ACTION_UP
                 || event.getAction() == MotionEvent.ACTION_CANCEL) {
             final float dy = event.getY() - mDownY;
-            setPeeked(mDownBottom + (int)dy, getExpandedBottom());
+            setPeeked(mDownBottom + (int)dy, getExpandedBottom(), true);
             if (mPeekable) {
                 close();
             }
@@ -347,14 +357,14 @@
         return rt;
     }
 
-    private void setPeeked(int peeked, int max) {
+    private void setPeeked(int peeked, int max, boolean isDone) {
         if (DEBUG) log("setPeeked=" + peeked);
         final int min = mHintText.getBottom();
         peeked = Math.max(min, Math.min(peeked, max));
-        if (mBottom == peeked) {
+        if (!isDone && mBottom == peeked) {
             return;
         }
-        if (peeked == max) {
+        if (peeked == max && isDone) {
             mPeekable = false;
             mModeSpinner.setEnabled(true);
             if (mAdapter != null) {
@@ -419,6 +429,14 @@
             }).start();
     }
 
+    private final Runnable mPopVibration = new Runnable() {
+        @Override
+        public void run() {
+            Vibrator v = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+            v.vibrate(mPopDuration);
+        }
+    };
+
     private final class UntilPager extends RelativeLayout {
         private final ImageView mPrev;
         private final ImageView mNext;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 8cd6e06..15a68ef 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -286,6 +286,7 @@
     int mUserRotation = Surface.ROTATION_0;
     boolean mAccelerometerDefault;
 
+    boolean mSupportAutoRotation;
     int mAllowAllRotations = -1;
     boolean mCarDockEnablesAccelerometer;
     boolean mDeskDockEnablesAccelerometer;
@@ -586,13 +587,15 @@
      * screen is switched off.
      */
     boolean needSensorRunningLp() {
-        if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
-                || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
-                || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
-                || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
-            // If the application has explicitly requested to follow the
-            // orientation, then we need to turn the sensor or.
-            return true;
+        if (mSupportAutoRotation) {
+            if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
+                    || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+                    || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
+                    || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
+                // If the application has explicitly requested to follow the
+                // orientation, then we need to turn the sensor on.
+                return true;
+            }
         }
         if ((mCarDockEnablesAccelerometer && mDockMode == Intent.EXTRA_DOCK_STATE_CAR) ||
                 (mDeskDockEnablesAccelerometer && (mDockMode == Intent.EXTRA_DOCK_STATE_DESK
@@ -613,7 +616,7 @@
             // still be turned off when the screen is off.)
             return false;
         }
-        return true;
+        return mSupportAutoRotation;
     }
 
     /*
@@ -878,6 +881,8 @@
         mBroadcastWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                 "PhoneWindowManager.mBroadcastWakeLock");
         mEnableShiftMenuBugReports = "1".equals(SystemProperties.get("ro.debuggable"));
+        mSupportAutoRotation = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_supportAutoRotation);
         mLidOpenRotation = readRotation(
                 com.android.internal.R.integer.config_lidOpenRotation);
         mCarDockRotation = readRotation(
@@ -3804,7 +3809,9 @@
                                                 mKeyguardDelegate.isShowingAndNotHidden() :
                                                 mKeyguardDelegate.isShowing()));
 
-        if (keyCode == KeyEvent.KEYCODE_POWER) {
+        if (keyCode == KeyEvent.KEYCODE_POWER
+                || keyCode == KeyEvent.KEYCODE_SLEEP
+                || keyCode == KeyEvent.KEYCODE_WAKEUP) {
             policyFlags |= WindowManagerPolicy.FLAG_WAKE;
         }
 
@@ -3981,7 +3988,7 @@
                                 telephonyService.silenceRinger();
                             } else if ((mIncallPowerBehavior
                                     & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
-                                    && telephonyService.isOffhook()) {
+                                    && telephonyService.isOffhook() && isScreenOn) {
                                 // Otherwise, if "Power button ends call" is enabled,
                                 // the Power button will hang up any current active call.
                                 hungUp = telephonyService.endCall();
@@ -4004,6 +4011,18 @@
                 break;
             }
 
+            case KeyEvent.KEYCODE_SLEEP: {
+                result &= ~ACTION_PASS_TO_USER;
+                mPowerManager.goToSleep(event.getEventTime());
+                isWakeKey = false;
+                break;
+            }
+
+            case KeyEvent.KEYCODE_WAKEUP: {
+                result &= ~ACTION_PASS_TO_USER;
+                break;
+            }
+
             case KeyEvent.KEYCODE_MEDIA_PLAY:
             case KeyEvent.KEYCODE_MEDIA_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -4488,6 +4507,10 @@
             } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
                 // Application just wants to remain locked in the last rotation.
                 preferredRotation = lastRotation;
+            } else if (!mSupportAutoRotation) {
+                // If we don't support auto-rotation then bail out here and ignore
+                // the sensor and any rotation lock settings.
+                preferredRotation = -1;
             } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
                             && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
                                     || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
@@ -5304,6 +5327,7 @@
             pw.print(prefix); pw.print("mLastFocusNeedsMenu=");
                     pw.println(mLastFocusNeedsMenu);
         }
+        pw.print(prefix); pw.print("mSupportAutoRotation="); pw.println(mSupportAutoRotation);
         pw.print(prefix); pw.print("mUiMode="); pw.print(mUiMode);
                 pw.print(" mDockMode="); pw.print(mDockMode);
                 pw.print(" mCarDockRotation="); pw.print(mCarDockRotation);
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index f03a8e0..9de3efe 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -426,8 +426,10 @@
         final Pair<String, ComponentName> mTarget;
         final BroadcastStats mBroadcastStats;
         final FilterStats mFilterStats;
+        final int mAlarmType;
 
-        InFlight(AlarmManagerService service, PendingIntent pendingIntent, WorkSource workSource) {
+        InFlight(AlarmManagerService service, PendingIntent pendingIntent, WorkSource workSource,
+                int alarmType) {
             mPendingIntent = pendingIntent;
             mWorkSource = workSource;
             Intent intent = pendingIntent.getIntent();
@@ -441,6 +443,7 @@
                 mBroadcastStats.filterStats.put(mTarget, fs);
             }
             mFilterStats = fs;
+            mAlarmType = alarmType;
         }
     }
 
@@ -1280,14 +1283,12 @@
                             
                             // we have an active broadcast so stay awake.
                             if (mBroadcastRefCount == 0) {
-                                setWakelockWorkSource(alarm.operation, alarm.workSource);
-                                mWakeLock.setUnimportantForLogging(
-                                        alarm.operation == mTimeTickSender);
-                                mWakeLock.setHistoryTag(alarm.operation.getTag("*alarm*:"));
+                                setWakelockWorkSource(alarm.operation, alarm.workSource,
+                                        alarm.type, true);
                                 mWakeLock.acquire();
                             }
                             final InFlight inflight = new InFlight(AlarmManagerService.this,
-                                    alarm.operation, alarm.workSource);
+                                    alarm.operation, alarm.workSource, alarm.type);
                             mInFlight.add(inflight);
                             mBroadcastRefCount++;
 
@@ -1342,9 +1343,17 @@
      * @param pi PendingIntent to attribute blame to if ws is null.
      * @param ws WorkSource to attribute blame.
      */
-    void setWakelockWorkSource(PendingIntent pi, WorkSource ws) {
+    void setWakelockWorkSource(PendingIntent pi, WorkSource ws, int type, boolean first) {
         try {
+            mWakeLock.setUnimportantForLogging(pi == mTimeTickSender);
             if (ws != null) {
+                if (first) {
+                    mWakeLock.setHistoryTag(pi.getTag(
+                            type == ELAPSED_REALTIME_WAKEUP || type == RTC_WAKEUP
+                                    ? "*walarm*:" : "*alarm*:"));
+                } else {
+                    mWakeLock.setHistoryTag(null);
+                }
                 mWakeLock.setWorkSource(ws);
                 return;
             }
@@ -1352,6 +1361,7 @@
             final int uid = ActivityManagerNative.getDefault()
                     .getUidForIntentSender(pi.getTarget());
             if (uid >= 0) {
+                mWakeLock.setHistoryTag(null);
                 mWakeLock.setWorkSource(new WorkSource(uid));
                 return;
             }
@@ -1576,7 +1586,8 @@
                     // the next of our alarms is now in flight.  reattribute the wakelock.
                     if (mInFlight.size() > 0) {
                         InFlight inFlight = mInFlight.get(0);
-                        setWakelockWorkSource(inFlight.mPendingIntent, inFlight.mWorkSource);
+                        setWakelockWorkSource(inFlight.mPendingIntent, inFlight.mWorkSource,
+                                inFlight.mAlarmType, false);
                     } else {
                         // should never happen
                         mLog.w("Alarm wakelock still held but sent queue empty");
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 0b3eb12..6827b3f 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -59,6 +59,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -78,6 +79,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.style.SuggestionSpan;
@@ -441,6 +443,10 @@
                 hideInputMethodMenu();
                 // No need to updateActive
                 return;
+            } else if (Intent.ACTION_USER_ADDED.equals(action)
+                    || Intent.ACTION_USER_REMOVED.equals(action)) {
+                updateRelatedUserIds();
+                return;
             } else {
                 Slog.w(TAG, "Unexpected intent " + intent);
             }
@@ -642,6 +648,8 @@
         broadcastFilter.addAction(Intent.ACTION_SCREEN_ON);
         broadcastFilter.addAction(Intent.ACTION_SCREEN_OFF);
         broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+        broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
+        broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
         mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
 
         mNotificationShown = false;
@@ -675,6 +683,7 @@
         // mSettings should be created before buildInputMethodListLocked
         mSettings = new InputMethodSettings(
                 mRes, context.getContentResolver(), mMethodMap, mMethodList, userId);
+        updateRelatedUserIds();
         mFileManager = new InputMethodFileManager(mMethodMap, userId);
         mSwitchingController = new InputMethodSubtypeSwitchingController(mSettings);
         mSwitchingController.resetCircularListLocked(context);
@@ -790,6 +799,7 @@
 
     private void switchUserLocked(int newUserId) {
         mSettings.setCurrentUserId(newUserId);
+        updateRelatedUserIds();
         // InputMethodFileManager should be reset when the user is changed
         mFileManager = new InputMethodFileManager(mMethodMap, newUserId);
         final String defaultImiId = mSettings.getSelectedInputMethod();
@@ -810,6 +820,16 @@
         }
     }
 
+    void updateRelatedUserIds() {
+        List<UserInfo> relatedUsers =
+                UserManager.get(mContext).getRelatedUsers(mSettings.getCurrentUserId());
+        int[] relatedUserIds = new int[relatedUsers.size()]; // relatedUsers will not be null
+        for (int i = 0; i < relatedUserIds.length; i++) {
+            relatedUserIds[i] = relatedUsers.get(i).id;
+        }
+        mSettings.setRelatedUserIds(relatedUserIds);
+    }
+
     @Override
     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
             throws RemoteException {
@@ -905,7 +925,7 @@
                     + mSettings.getCurrentUserId() + ", calling pid = " + Binder.getCallingPid()
                     + InputMethodUtils.getApiCallStack());
         }
-        if (uid == Process.SYSTEM_UID || userId == mSettings.getCurrentUserId()) {
+        if (uid == Process.SYSTEM_UID || mSettings.isRelatedToOrCurrentUser(userId)) {
             return true;
         }
 
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index a7ef424..cd74fed 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -80,7 +80,6 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
 import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
 import java.security.NoSuchAlgorithmException;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d910861..128f636 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1015,6 +1015,7 @@
     final ActivityThread mSystemThread;
 
     int mCurrentUserId = 0;
+    int[] mRelatedUserIds = new int[0]; // Accessed by ActivityStack
     private UserManagerService mUserManager;
 
     private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -7244,6 +7245,24 @@
     }
 
     @Override
+    public boolean isInHomeStack(int taskId) {
+        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "getStackInfo()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                TaskRecord tr = recentTaskForIdLocked(taskId);
+                if (tr != null) {
+                    return tr.stack.isHomeStack();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return false;
+    }
+
+    @Override
     public int getTaskForActivity(IBinder token, boolean onlyRoot) {
         synchronized(this) {
             return ActivityRecord.getTaskForActivityLocked(token, onlyRoot);
@@ -16127,6 +16146,20 @@
         return startUser(userId, /* foreground */ false);
     }
 
+    /**
+     * Refreshes the list of users related to the current user when either a
+     * user switch happens or when a new related user is started in the
+     * background.
+     */
+    private void updateRelatedUserIdsLocked() {
+        final List<UserInfo> relatedUsers = getUserManagerLocked().getRelatedUsers(mCurrentUserId);
+        int[] relatedUserIds = new int[relatedUsers.size()]; // relatedUsers will not be null
+        for (int i = 0; i < relatedUserIds.length; i++) {
+            relatedUserIds[i] = relatedUsers.get(i).id;
+        }
+        mRelatedUserIds = relatedUserIds;
+    }
+
     @Override
     public boolean switchUser(final int userId) {
         return startUser(userId, /* foregound */ true);
@@ -16180,12 +16213,15 @@
 
                 if (foreground) {
                     mCurrentUserId = userId;
-                    mWindowManager.setCurrentUser(userId);
+                    updateRelatedUserIdsLocked();
+                    mWindowManager.setCurrentUser(userId, mRelatedUserIds);
                     // Once the internal notion of the active user has switched, we lock the device
                     // with the option to show the user switcher on the keyguard.
                     mWindowManager.lockNow(null);
                 } else {
                     final Integer currentUserIdInt = Integer.valueOf(mCurrentUserId);
+                    updateRelatedUserIdsLocked();
+                    mWindowManager.updateRelatedUserIds(mRelatedUserIds);
                     mUserLru.remove(currentUserIdInt);
                     mUserLru.add(currentUserIdInt);
                 }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 922cef4..087ad83c 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -71,6 +71,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.util.EventLog;
@@ -341,8 +342,19 @@
         mCurrentUser = mService.mCurrentUserId;
     }
 
-    boolean okToShow(ActivityRecord r) {
-        return r.userId == mCurrentUser
+    /**
+     * Checks whether the userid is either the current user or a related user.
+     */
+    private boolean isRelatedToOrCurrentUserLocked(int userId) {
+        if (mCurrentUser == userId) return true;
+        for (int i = 0; i < mService.mRelatedUserIds.length; i++) {
+            if (mService.mRelatedUserIds[i] == userId) return true;
+        }
+        return false;
+    }
+
+    boolean okToShowLocked(ActivityRecord r) {
+        return isRelatedToOrCurrentUserLocked(r.userId)
                 || (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0;
     }
 
@@ -362,7 +374,7 @@
             final ArrayList<ActivityRecord> activities = task.mActivities;
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 ActivityRecord r = activities.get(activityNdx);
-                if (!r.finishing && !r.delayedResume && r != notTop && okToShow(r)) {
+                if (!r.finishing && !r.delayedResume && r != notTop && okToShowLocked(r)) {
                     return r;
                 }
             }
@@ -389,7 +401,7 @@
             for (int i = activities.size() - 1; i >= 0; --i) {
                 final ActivityRecord r = activities.get(i);
                 // Note: the taskId check depends on real taskId fields being non-zero
-                if (!r.finishing && (token != r.appToken) && okToShow(r)) {
+                if (!r.finishing && (token != r.appToken) && okToShowLocked(r)) {
                     return r;
                 }
             }
@@ -542,7 +554,7 @@
 
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             TaskRecord task = mTaskHistory.get(taskNdx);
-            if (task.userId != mCurrentUser) {
+            if (!isRelatedToOrCurrentUserLocked(task.userId)) {
                 return null;
             }
             final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -573,7 +585,7 @@
         int index = mTaskHistory.size();
         for (int i = 0; i < index; ) {
             TaskRecord task = mTaskHistory.get(i);
-            if (task.userId == userId) {
+            if (isRelatedToOrCurrentUserLocked(task.userId)) {
                 if (DEBUG_TASKS) Slog.d(TAG, "switchUserLocked: stack=" + getStackId() +
                         " moving " + task + " to top");
                 mTaskHistory.remove(i);
@@ -704,10 +716,17 @@
         int w = mThumbnailWidth;
         int h = mThumbnailHeight;
         if (w < 0) {
-            mThumbnailWidth = w =
-                res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
-            mThumbnailHeight = h =
-                res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
+            if (SystemProperties.getBoolean("persist.recents.use_alternate", false)) {
+                mThumbnailWidth = w =
+                   res.getDimensionPixelSize(com.android.internal.R.dimen.recents_thumbnail_width);
+                mThumbnailHeight = h =
+                   res.getDimensionPixelSize(com.android.internal.R.dimen.recents_thumbnail_height);
+            } else {
+                mThumbnailWidth = w =
+                    res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+                mThumbnailHeight = h =
+                    res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
+            }
         }
 
         if (w > 0) {
@@ -1728,10 +1747,10 @@
         mTaskHistory.remove(task);
         // Now put task at top.
         int stackNdx = mTaskHistory.size();
-        if (task.userId != mCurrentUser) {
+        if (!isRelatedToOrCurrentUserLocked(task.userId)) {
             // Put non-current user tasks below current user tasks.
             while (--stackNdx >= 0) {
-                if (mTaskHistory.get(stackNdx).userId != mCurrentUser) {
+                if (!isRelatedToOrCurrentUserLocked(mTaskHistory.get(stackNdx).userId)) {
                     break;
                 }
             }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index fc7aac2..3414daf 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -29,6 +29,7 @@
 import android.os.Parcel;
 import android.os.Process;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.telephony.SignalStrength;
@@ -48,6 +49,8 @@
  * battery life.
  */
 public final class BatteryStatsService extends IBatteryStats.Stub {
+    static final String TAG = "BatteryStatsService";
+
     static IBatteryStats sService;
     
     final BatteryStatsImpl mStats;
@@ -66,7 +69,8 @@
         mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_radioScanningTimeout)
                 * 1000L);
-    }
+        (new WakeupReasonThread()).start();
+     }
     
     public void shutdown() {
         Slog.w("BatteryStats", "Writing battery stats before shutdown...");
@@ -130,14 +134,15 @@
             boolean unimportantForLogging) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.noteStartWakeLocked(uid, pid, name, historyName, type, unimportantForLogging);
+            mStats.noteStartWakeLocked(uid, pid, name, historyName, type, unimportantForLogging,
+                    SystemClock.elapsedRealtime());
         }
     }
 
     public void noteStopWakelock(int uid, int pid, String name, int type) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.noteStopWakeLocked(uid, pid, name, type);
+            mStats.noteStopWakeLocked(uid, pid, name, type, SystemClock.elapsedRealtime());
         }
     }
 
@@ -150,6 +155,16 @@
         }
     }
 
+    public void noteChangeWakelockFromSource(WorkSource ws, int pid, String name, int type,
+            WorkSource newWs, int newPid, String newName,
+            String newHistoryName, int newType, boolean newUnimportantForLogging) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteChangeWakelockFromSourceLocked(ws, pid, name, type,
+                    newWs, newPid, newName, newHistoryName, newType, newUnimportantForLogging);
+        }
+    }
+
     public void noteStopWakelockFromSource(WorkSource ws, int pid, String name, int type) {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -538,14 +553,45 @@
         mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
                 Binder.getCallingPid(), Binder.getCallingUid(), null);
     }
-    
+
+    final class WakeupReasonThread extends Thread {
+        final int[] mIrqs = new int[32];
+        final String[] mReasons = new String[32];
+
+        WakeupReasonThread() {
+            super("BatteryStats_wakeupReason");
+        }
+
+        public void run() {
+            Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+
+            try {
+                int num;
+                while ((num=nativeWaitWakeup(mIrqs, mReasons)) >= 0) {
+                    synchronized (mStats) {
+                        for (int i=0; i<num; i++) {
+                            //Slog.i(TAG, "Wakeup: irq #" + mIrqs[i] + " reason=" + mReasons[i]);
+                            mStats.noteWakeupReasonLocked(mIrqs[i], mReasons[i]);
+                        }
+                    }
+                }
+            } catch (RuntimeException e) {
+                Slog.e(TAG, "Failure reading wakeup reasons", e);
+            }
+        }
+    }
+
+    private static native int nativeWaitWakeup(int[] outIrqs, String[] outReasons);
+
     private void dumpHelp(PrintWriter pw) {
         pw.println("Battery stats (batterystats) dump options:");
-        pw.println("  [--checkin] [--history] [-c] [--unplugged] [--reset] [--write]");
-        pw.println("  [-h] [<package.name>]");
+        pw.println("  [--checkin] [--history] [--history-start] [--unplugged] [--charged] [-c]");
+        pw.println("  [--reset] [--write] [-h] [<package.name>]");
         pw.println("  --checkin: format output for a checkin report.");
         pw.println("  --history: show only history data.");
+        pw.println("  --history-start <num>: show only history data starting at given time offset.");
         pw.println("  --unplugged: only output data since last unplugged.");
+        pw.println("  --charged: only output data since last charged.");
         pw.println("  --reset: reset the stats, clearing all current data.");
         pw.println("  --write: force write current collected stats to disk.");
         pw.println("  -h: print this help text.");
@@ -562,23 +608,34 @@
             return;
         }
 
+        int flags = 0;
         boolean isCheckin = false;
-        boolean includeHistory = false;
-        boolean historyOnly = false;
-        boolean isUnpluggedOnly = false;
         boolean noOutput = false;
+        long historyStart = -1;
         int reqUid = -1;
         if (args != null) {
-            for (String arg : args) {
+            for (int i=0; i<args.length; i++) {
+                String arg = args[i];
                 if ("--checkin".equals(arg)) {
                     isCheckin = true;
                 } else if ("--history".equals(arg)) {
-                    historyOnly = true;
+                    flags |= BatteryStats.DUMP_HISTORY_ONLY;
+                } else if ("--history-start".equals(arg)) {
+                    flags |= BatteryStats.DUMP_HISTORY_ONLY;
+                    i++;
+                    if (i >= args.length) {
+                        pw.println("Missing time argument for --history-since");
+                        dumpHelp(pw);
+                        return;
+                    }
+                    historyStart = Long.parseLong(args[i]);
                 } else if ("-c".equals(arg)) {
                     isCheckin = true;
-                    includeHistory = true;
+                    flags |= BatteryStats.DUMP_INCLUDE_HISTORY;
                 } else if ("--unplugged".equals(arg)) {
-                    isUnpluggedOnly = true;
+                    flags |= BatteryStats.DUMP_UNPLUGGED_ONLY;
+                } else if ("--charged".equals(arg)) {
+                    flags |= BatteryStats.DUMP_CHARGED_ONLY;
                 } else if ("--reset".equals(arg)) {
                     synchronized (mStats) {
                         mStats.resetAllStatsCmdLocked();
@@ -619,12 +676,11 @@
         if (isCheckin) {
             List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
             synchronized (mStats) {
-                mStats.dumpCheckinLocked(mContext, pw, apps, isUnpluggedOnly, includeHistory,
-                        historyOnly);
+                mStats.dumpCheckinLocked(mContext, pw, apps, flags, historyStart);
             }
         } else {
             synchronized (mStats) {
-                mStats.dumpLocked(mContext, pw, isUnpluggedOnly, reqUid, historyOnly);
+                mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 9740812..3a43521 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -153,7 +153,7 @@
     ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
         for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
             ActivityRecord r = mActivities.get(activityNdx);
-            if (!r.finishing && r != notTop && stack.okToShow(r)) {
+            if (!r.finishing && r != notTop && stack.okToShowLocked(r)) {
                 return r;
             }
         }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index e49382e..316bd57 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -52,6 +52,7 @@
 import android.hardware.input.InputManager;
 import android.hardware.input.InputManagerInternal;
 import android.hardware.input.KeyboardLayout;
+import android.hardware.input.TouchCalibration;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -76,6 +77,7 @@
 import android.view.InputEvent;
 import android.view.KeyEvent;
 import android.view.PointerIcon;
+import android.view.Surface;
 import android.view.ViewConfiguration;
 import android.view.WindowManagerPolicy;
 import android.widget.Toast;
@@ -183,6 +185,7 @@
             InputChannel fromChannel, InputChannel toChannel);
     private static native void nativeSetPointerSpeed(long ptr, int speed);
     private static native void nativeSetShowTouches(long ptr, boolean enabled);
+    private static native void nativeReloadCalibration(long ptr);
     private static native void nativeVibrate(long ptr, int deviceId, long[] pattern,
             int repeat, int token);
     private static native void nativeCancelVibrate(long ptr, int deviceId, int token);
@@ -700,6 +703,47 @@
         mTempFullKeyboards.clear();
     }
 
+    @Override // Binder call & native callback
+    public TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor,
+            int surfaceRotation) {
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
+
+        synchronized (mDataStore) {
+            return mDataStore.getTouchCalibration(inputDeviceDescriptor, surfaceRotation);
+        }
+    }
+
+    @Override // Binder call
+    public void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int surfaceRotation,
+            TouchCalibration calibration) {
+        if (!checkCallingPermission(android.Manifest.permission.SET_INPUT_CALIBRATION,
+                "setTouchCalibrationForInputDevice()")) {
+            throw new SecurityException("Requires SET_INPUT_CALIBRATION permission");
+        }
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
+        if (calibration == null) {
+            throw new IllegalArgumentException("calibration must not be null");
+        }
+        if (surfaceRotation < Surface.ROTATION_0 || surfaceRotation > Surface.ROTATION_270) {
+            throw new IllegalArgumentException("surfaceRotation value out of bounds");
+        }
+
+        synchronized (mDataStore) {
+            try {
+                if (mDataStore.setTouchCalibration(inputDeviceDescriptor, surfaceRotation,
+                        calibration)) {
+                    nativeReloadCalibration(mPtr);
+                }
+            } finally {
+                mDataStore.saveIfNeeded();
+            }
+        }
+    }
+
     // Must be called on handler.
     private void showMissingKeyboardLayoutNotification() {
         if (!mKeyboardLayoutNotificationShown) {
diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java
index 71de776..92fa813 100644
--- a/services/core/java/com/android/server/input/PersistentDataStore.java
+++ b/services/core/java/com/android/server/input/PersistentDataStore.java
@@ -24,6 +24,8 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import android.view.Surface;
+import android.hardware.input.TouchCalibration;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.Xml;
@@ -82,6 +84,30 @@
         }
     }
 
+    public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
+        if (state == null) {
+            return TouchCalibration.IDENTITY;
+        }
+
+        TouchCalibration cal = state.getTouchCalibration(surfaceRotation);
+        if (cal == null) {
+            return TouchCalibration.IDENTITY;
+        }
+        return cal;
+    }
+
+    public boolean setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
+
+        if (state.setTouchCalibration(surfaceRotation, calibration)) {
+            setDirty();
+            return true;
+        }
+
+        return false;
+    }
+
     public String getCurrentKeyboardLayout(String inputDeviceDescriptor) {
         InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
         return state != null ? state.getCurrentKeyboardLayout() : null;
@@ -275,9 +301,35 @@
     }
 
     private static final class InputDeviceState {
+        private static final String[] CALIBRATION_NAME = { "x_scale",
+                "x_ymix", "x_offset", "y_xmix", "y_scale", "y_offset" };
+
+        private TouchCalibration[] mTouchCalibration = new TouchCalibration[4];
         private String mCurrentKeyboardLayout;
         private ArrayList<String> mKeyboardLayouts = new ArrayList<String>();
 
+        public TouchCalibration getTouchCalibration(int surfaceRotation) {
+            try {
+                return mTouchCalibration[surfaceRotation];
+            } catch (ArrayIndexOutOfBoundsException ex) {
+                Slog.w(InputManagerService.TAG, "Cannot get touch calibration.", ex);
+                return null;
+            }
+        }
+
+        public boolean setTouchCalibration(int surfaceRotation, TouchCalibration calibration) {
+            try {
+                if (!calibration.equals(mTouchCalibration[surfaceRotation])) {
+                    mTouchCalibration[surfaceRotation] = calibration;
+                    return true;
+                }
+                return false;
+            } catch (ArrayIndexOutOfBoundsException ex) {
+                Slog.w(InputManagerService.TAG, "Cannot set touch calibration.", ex);
+                return false;
+            }
+        }
+
         public String getCurrentKeyboardLayout() {
             return mCurrentKeyboardLayout;
         }
@@ -389,6 +441,52 @@
                         }
                         mCurrentKeyboardLayout = descriptor;
                     }
+                } else if (parser.getName().equals("calibration")) {
+                    String format = parser.getAttributeValue(null, "format");
+                    String rotation = parser.getAttributeValue(null, "rotation");
+                    int r = -1;
+
+                    if (format == null) {
+                        throw new XmlPullParserException(
+                                "Missing format attribute on calibration.");
+                    }
+                    if (!format.equals("affine")) {
+                        throw new XmlPullParserException(
+                                "Unsupported format for calibration.");
+                    }
+                    if (rotation != null) {
+                        try {
+                            r = stringToSurfaceRotation(rotation);
+                        } catch (IllegalArgumentException e) {
+                            throw new XmlPullParserException(
+                                    "Unsupported rotation for calibration.");
+                        }
+                    }
+
+                    float[] matrix = TouchCalibration.IDENTITY.getAffineTransform();
+                    int depth = parser.getDepth();
+                    while (XmlUtils.nextElementWithin(parser, depth)) {
+                        String tag = parser.getName().toLowerCase();
+                        String value = parser.nextText();
+
+                        for (int i = 0; i < matrix.length && i < CALIBRATION_NAME.length; i++) {
+                            if (tag.equals(CALIBRATION_NAME[i])) {
+                                matrix[i] = Float.parseFloat(value);
+                                break;
+                            }
+                        }
+                    }
+
+                    if (r == -1) {
+                        // Assume calibration applies to all rotations
+                        for (r = 0; r < mTouchCalibration.length; r++) {
+                            mTouchCalibration[r] = new TouchCalibration(matrix[0],
+                                matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
+                        }
+                    } else {
+                        mTouchCalibration[r] = new TouchCalibration(matrix[0],
+                            matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
+                    }
                 }
             }
 
@@ -411,6 +509,49 @@
                 }
                 serializer.endTag(null, "keyboard-layout");
             }
+
+            for (int i = 0; i < mTouchCalibration.length; i++) {
+                if (mTouchCalibration[i] != null) {
+                    String rotation = surfaceRotationToString(i);
+                    float[] transform = mTouchCalibration[i].getAffineTransform();
+
+                    serializer.startTag(null, "calibration");
+                    serializer.attribute(null, "format", "affine");
+                    serializer.attribute(null, "rotation", rotation);
+                    for (int j = 0; j < transform.length && j < CALIBRATION_NAME.length; j++) {
+                        serializer.startTag(null, CALIBRATION_NAME[j]);
+                        serializer.text(Float.toString(transform[j]));
+                        serializer.endTag(null, CALIBRATION_NAME[j]);
+                    }
+                    serializer.endTag(null, "calibration");
+                }
+            }
+        }
+
+        private static String surfaceRotationToString(int surfaceRotation) {
+            switch (surfaceRotation) {
+                case Surface.ROTATION_0:   return "0";
+                case Surface.ROTATION_90:  return "90";
+                case Surface.ROTATION_180: return "180";
+                case Surface.ROTATION_270: return "270";
+            }
+            throw new IllegalArgumentException("Unsupported surface rotation value" + surfaceRotation);
+        }
+
+        private static int stringToSurfaceRotation(String s) {
+            if ("0".equals(s)) {
+                return Surface.ROTATION_0;
+            }
+            if ("90".equals(s)) {
+                return Surface.ROTATION_90;
+            }
+            if ("180".equals(s)) {
+                return Surface.ROTATION_180;
+            }
+            if ("270".equals(s)) {
+                return Surface.ROTATION_270;
+            }
+            throw new IllegalArgumentException("Unsupported surface rotation string '" + s + "'");
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index ad6eabd..b7e367b 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -218,6 +218,19 @@
         builder.append(' ');
         builder.append(uid);
         builder.append(isPublic ? " 1" : " 0");
+        builder.append(" *");         // No pkgName arg present
+        return execute(builder.toString());
+    }
+
+    public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName) {
+        StringBuilder builder = new StringBuilder("dexopt");
+        builder.append(' ');
+        builder.append(apkPath);
+        builder.append(' ');
+        builder.append(uid);
+        builder.append(isPublic ? " 1" : " 0");
+        builder.append(' ');
+        builder.append(pkgName);
         return execute(builder.toString());
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7f00ce7..cbcf408 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1216,7 +1216,7 @@
                         continue;
                     }
                     try {
-                        if (dalvik.system.DexFile.isDexOptNeeded(lib)) {
+                        if (dalvik.system.DexFile.isDexOptNeededInternal(lib, null, false)) {
                             alreadyDexOpted.add(lib);
                             mInstaller.dexopt(lib, Process.SYSTEM_UID, true);
                             didDexOpt = true;
@@ -1260,7 +1260,7 @@
                         continue;
                     }
                     try {
-                        if (dalvik.system.DexFile.isDexOptNeeded(path)) {
+                        if (dalvik.system.DexFile.isDexOptNeededInternal(path, null, false)) {
                             mInstaller.dexopt(path, Process.SYSTEM_UID, true);
                             didDexOpt = true;
                         }
@@ -4088,7 +4088,8 @@
             String path = pkg.mScanPath;
             int ret = 0;
             try {
-                if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) {
+                if (forceDex || dalvik.system.DexFile.isDexOptNeededInternal(path, pkg.packageName,
+                                                                             defer)) {
                     if (!forceDex && defer) {
                         if (mDeferredDexOpt == null) {
                             mDeferredDexOpt = new HashSet<PackageParser.Package>();
@@ -4098,7 +4099,8 @@
                     } else {
                         Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName);
                         final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
-                        ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg));
+                        ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg),
+                                                pkg.packageName);
                         pkg.mDidDexOpt = true;
                         performed = true;
                     }
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index e1ccf46..df06bae 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -158,6 +158,39 @@
     }
 
     /**
+     * Called when a wake lock is changing.
+     */
+    public void onWakeLockChanging(int flags, String tag, String packageName,
+            int ownerUid, int ownerPid, WorkSource workSource, String historyTag,
+            int newFlags, String newTag, String newPackageName, int newOwnerUid,
+            int newOwnerPid, WorkSource newWorkSource, String newHistoryTag) {
+
+        final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+        final int newMonitorType = getBatteryStatsWakeLockMonitorType(newFlags);
+        boolean unimportantForLogging = (flags&PowerManager.UNIMPORTANT_FOR_LOGGING) != 0
+                && ownerUid == Process.SYSTEM_UID;
+        if (workSource != null && newWorkSource != null) {
+            if (DEBUG) {
+                Slog.d(TAG, "onWakeLockChanging: flags=" + newFlags + ", tag=\"" + newTag
+                        + "\", packageName=" + newPackageName
+                        + ", ownerUid=" + newOwnerUid + ", ownerPid=" + newOwnerPid
+                        + ", workSource=" + newWorkSource);
+            }
+            try {
+                mBatteryStats.noteChangeWakelockFromSource(workSource, ownerPid, tag, monitorType,
+                        newWorkSource, newOwnerPid, newTag, newHistoryTag,
+                        newMonitorType, unimportantForLogging);
+            } catch (RemoteException ex) {
+                // Ignore
+            }
+        } else {
+            onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource);
+            onWakeLockAcquired(newFlags, newTag, newPackageName, newOwnerUid, newOwnerPid,
+                    newWorkSource, newHistoryTag);
+        }
+    }
+
+    /**
      * Called when a wake lock is released.
      */
     public void onWakeLockReleased(int flags, String tag, String packageName,
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index e7bbf1c..40ebe8d 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -646,9 +646,9 @@
                 wakeLock = mWakeLocks.get(index);
                 if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
                     // Update existing wake lock.  This shouldn't happen but is harmless.
-                    notifyWakeLockReleasedLocked(wakeLock);
+                    notifyWakeLockChangingLocked(wakeLock, flags, tag, packageName,
+                            uid, pid, ws, historyTag);
                     wakeLock.updateProperties(flags, tag, packageName, ws, historyTag, uid, pid);
-                    notifyWakeLockAcquiredLocked(wakeLock);
                 }
             } else {
                 wakeLock = new WakeLock(lock, flags, tag, packageName, ws, historyTag, uid, pid);
@@ -765,9 +765,10 @@
             }
 
             if (!wakeLock.hasSameWorkSource(ws)) {
-                notifyWakeLockReleasedLocked(wakeLock);
+                notifyWakeLockChangingLocked(wakeLock, wakeLock.mFlags, wakeLock.mTag,
+                        wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid,
+                        ws, wakeLock.mHistoryTag);
                 wakeLock.updateWorkSource(ws);
-                notifyWakeLockAcquiredLocked(wakeLock);
             }
         }
     }
@@ -791,6 +792,15 @@
         }
     }
 
+    private void notifyWakeLockChangingLocked(WakeLock wakeLock, int flags, String tag,
+            String packageName, int uid, int pid, WorkSource ws, String historyTag) {
+        if (mSystemReady && wakeLock.mNotifiedAcquired) {
+            mNotifier.onWakeLockChanging(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName,
+                    wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource,
+                    wakeLock.mHistoryTag, flags, tag, packageName, uid, pid, ws, historyTag);
+        }
+    }
+
     private void notifyWakeLockReleasedLocked(WakeLock wakeLock) {
         if (mSystemReady && wakeLock.mNotifiedAcquired) {
             wakeLock.mNotifiedAcquired = false;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 97ea52c..87953fe 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -230,7 +230,6 @@
         public void onServiceConnected(ComponentName name, IBinder service) {
             synchronized (mLock) {
                 if (mWallpaper.connection == this) {
-                    mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
                     mService = IWallpaperService.Stub.asInterface(service);
                     attachServiceLocked(this, mWallpaper);
                     // XXX should probably do saveSettingsLocked() later
@@ -250,11 +249,21 @@
                 if (mWallpaper.connection == this) {
                     Slog.w(TAG, "Wallpaper service gone: " + mWallpaper.wallpaperComponent);
                     if (!mWallpaper.wallpaperUpdating
-                            && (mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME)
-                                > SystemClock.uptimeMillis()
                             && mWallpaper.userId == mCurrentUserId) {
-                        Slog.w(TAG, "Reverting to built-in wallpaper!");
-                        clearWallpaperLocked(true, mWallpaper.userId, null);
+                        // There is a race condition which causes
+                        // {@link #mWallpaper.wallpaperUpdating} to be false even if it is
+                        // currently updating since the broadcast notifying us is async.
+                        // This race is overcome by the general rule that we only reset the
+                        // wallpaper if its service was shut down twice
+                        // during {@link #MIN_WALLPAPER_CRASH_TIME} millis.
+                        if (mWallpaper.lastDiedTime != 0
+                                && mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
+                                    > SystemClock.uptimeMillis()) {
+                            Slog.w(TAG, "Reverting to built-in wallpaper!");
+                            clearWallpaperLocked(true, mWallpaper.userId, null);
+                        } else {
+                            mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
+                        }
                     }
                 }
             }
@@ -938,7 +947,6 @@
             }
             wallpaper.wallpaperComponent = componentName;
             wallpaper.connection = newConn;
-            wallpaper.lastDiedTime = SystemClock.uptimeMillis();
             newConn.mReply = reply;
             try {
                 if (wallpaper.userId == mCurrentUserId) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c006613..be94ca7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -19,7 +19,6 @@
 import static android.view.WindowManager.LayoutParams.*;
 
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-
 import android.app.AppOpsManager;
 import android.util.ArraySet;
 import android.util.TimeUtils;
@@ -302,8 +301,16 @@
         }
     };
 
-    // Current user when multi-user is enabled. Don't show windows of non-current user.
+    /**
+     * Current user when multi-user is enabled. Don't show windows of
+     * non-current user. Also see mRelatedUserIds.
+     */
     int mCurrentUserId;
+    /**
+     * Users related to the current user. These are also allowed to show windows
+     * on the current user.
+     */
+    int[] mRelatedUserIds = new int[0];
 
     final Context mContext;
 
@@ -5284,10 +5291,16 @@
         mPolicy.setTouchExplorationEnabled(enabled);
     }
 
-    public void setCurrentUser(final int newUserId) {
+    public void updateRelatedUserIds(final int[] relatedUserIds) {
         synchronized (mWindowMap) {
-            int oldUserId = mCurrentUserId;
+            mRelatedUserIds = relatedUserIds;
+        }
+    }
+
+    public void setCurrentUser(final int newUserId, final int[] relatedUserIds) {
+        synchronized (mWindowMap) {
             mCurrentUserId = newUserId;
+            mRelatedUserIds = relatedUserIds;
             mAppTransition.setCurrentUser(newUserId);
             mPolicy.setCurrentUserLw(newUserId);
 
@@ -5302,6 +5315,15 @@
         }
     }
 
+    /* Called by WindowState */
+    boolean isRelatedToOrCurrentUserLocked(int userId) {
+        if (userId == mCurrentUserId) return true;
+        for (int i = 0; i < mRelatedUserIds.length; i++) {
+            if (mRelatedUserIds[i] == userId) return true;
+        }
+        return false;
+    }
+
     public void enableScreenAfterBoot() {
         synchronized(mWindowMap) {
             if (DEBUG_BOOT) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a8e45c4..2c0e99e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1249,7 +1249,7 @@
         }
 
         return win.mShowToOwnerOnly
-                && UserHandle.getUserId(win.mOwnerUid) != mService.mCurrentUserId;
+                && !mService.isRelatedToOrCurrentUserLocked(UserHandle.getUserId(win.mOwnerUid));
     }
 
     private static void applyInsets(Region outRegion, Rect frame, Rect inset) {
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 56144b0..0607925 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -6,6 +6,7 @@
 
 LOCAL_SRC_FILES += \
     $(LOCAL_REL_DIR)/com_android_server_AlarmManagerService.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_am_BatteryStatsService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_dreams_McuHal.cpp \
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
new file mode 100644
index 0000000..22cc519
--- /dev/null
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "BatteryStatsService"
+//#define LOG_NDEBUG 0
+
+#include <android_runtime/AndroidRuntime.h>
+#include <jni.h>
+
+#include <ScopedLocalRef.h>
+#include <ScopedPrimitiveArray.h>
+
+#include <cutils/log.h>
+#include <utils/misc.h>
+#include <utils/Log.h>
+#include <hardware/hardware.h>
+#include <suspend/autosuspend.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace android
+{
+
+#define LAST_RESUME_REASON "/sys/kernel/wakeup_reasons/last_resume_reason"
+#define MAX_REASON_SIZE 512
+
+static bool wakeup_init = false;
+static sem_t wakeup_sem;
+
+static void wakeup_callback(void)
+{
+    ALOGV("In wakeup_callback");
+    int ret = sem_post(&wakeup_sem);
+    if (ret < 0) {
+        char buf[80];
+        strerror_r(errno, buf, sizeof(buf));
+        ALOGE("Error posting wakeup sem: %s\n", buf);
+    }
+}
+
+static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jintArray outIrqs,
+        jobjectArray outReasons)
+{
+    bool first_time = false;
+
+    if (outIrqs == NULL || outReasons == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", "null argument");
+        return -1;
+    }
+
+    // Register our wakeup callback if not yet done.
+    if (!wakeup_init) {
+        wakeup_init = true;
+        ALOGV("Creating semaphore...");
+        int ret = sem_init(&wakeup_sem, 0, 0);
+        if (ret < 0) {
+            char buf[80];
+            strerror_r(errno, buf, sizeof(buf));
+            ALOGE("Error creating semaphore: %s\n", buf);
+            jniThrowException(env, "java/lang/IllegalStateException", buf);
+            return -1;
+        }
+        ALOGV("Registering callback...");
+        set_wakeup_callback(&wakeup_callback);
+        // First time through, we will just drain the current wakeup reasons.
+        first_time = true;
+    } else {
+        // On following calls, we need to wait for wakeup.
+        ALOGV("Waiting for wakeup...");
+        int ret = sem_wait(&wakeup_sem);
+        if (ret < 0) {
+            char buf[80];
+            strerror_r(errno, buf, sizeof(buf));
+            ALOGE("Error waiting on semaphore: %s\n", buf);
+            // Return 0 here to let it continue looping but not return results.
+            return 0;
+        }
+    }
+
+    FILE *fp = fopen(LAST_RESUME_REASON, "r");
+    if (fp == NULL) {
+        ALOGE("Failed to open %s", LAST_RESUME_REASON);
+        return -1;
+    }
+
+    int numOut = env->GetArrayLength(outIrqs);
+    ScopedIntArrayRW irqs(env, outIrqs);
+
+    ALOGV("Reading up to %d wakeup reasons", numOut);
+
+    char mergedreason[MAX_REASON_SIZE];
+    char* mergedreasonpos = mergedreason;
+    int remainreasonlen = MAX_REASON_SIZE;
+    int firstirq = 0;
+    char reasonline[128];
+    int i = 0;
+    while (fgets(reasonline, sizeof(reasonline), fp) != NULL && i < numOut) {
+        char* pos = reasonline;
+        char* endPos;
+        // First field is the index.
+        int irq = (int)strtol(pos, &endPos, 10);
+        if (pos == endPos) {
+            // Ooops.
+            ALOGE("Bad reason line: %s", reasonline);
+            continue;
+        }
+        pos = endPos;
+        // Skip whitespace; rest of the buffer is the reason string.
+        while (*pos == ' ') {
+            pos++;
+        }
+        // Chop newline at end.
+        char* endpos = pos;
+        while (*endpos != 0) {
+            if (*endpos == '\n') {
+                *endpos = 0;
+                break;
+            }
+            endpos++;
+        }
+        if (i == 0) {
+            firstirq = irq;
+        } else {
+            int len = snprintf(mergedreasonpos, remainreasonlen, ":%d", irq);
+            if (len >= 0 && len < remainreasonlen) {
+                mergedreasonpos += len;
+                remainreasonlen -= len;
+            }
+        }
+        int len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%s" : ":%s", pos);
+        if (len >= 0 && len < remainreasonlen) {
+            mergedreasonpos += len;
+            remainreasonlen -= len;
+        }
+        // For now it is better to combine all of these in to one entry in the
+        // battery history.  In the future, it might be nice to figure out a way
+        // to efficiently store multiple lines as a single entry in the history.
+        //irqs[i] = irq;
+        //ScopedLocalRef<jstring> reasonString(env, env->NewStringUTF(pos));
+        //env->SetObjectArrayElement(outReasons, i, reasonString.get());
+        //ALOGV("Wakeup reason #%d: irw %d reason %s", i, irq, pos);
+        i++;
+    }
+
+    ALOGV("Got %d reasons", i);
+    if (first_time) {
+        i = 0;
+    }
+    if (i > 0) {
+        irqs[0] = firstirq;
+        *mergedreasonpos = 0;
+        ScopedLocalRef<jstring> reasonString(env, env->NewStringUTF(mergedreason));
+        env->SetObjectArrayElement(outReasons, 0, reasonString.get());
+        i = 1;
+    }
+
+    if (fclose(fp) != 0) {
+        ALOGE("Failed to close %s", LAST_RESUME_REASON);
+        return -1;
+    }
+
+    return first_time ? 0 : i;
+}
+
+static JNINativeMethod method_table[] = {
+    { "nativeWaitWakeup", "([I[Ljava/lang/String;)I", (void*)nativeWaitWakeup },
+};
+
+int register_android_server_BatteryStatsService(JNIEnv *env)
+{
+    return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService",
+            method_table, NELEM(method_table));
+}
+
+};
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 0207c55..e1069ae 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -49,6 +49,7 @@
 #include <android/graphics/GraphicsJNI.h>
 
 #include <ScopedLocalRef.h>
+#include <ScopedPrimitiveArray.h>
 #include <ScopedUtfChars.h>
 
 #include "com_android_server_power_PowerManagerService.h"
@@ -86,6 +87,7 @@
     jmethodID getPointerIcon;
     jmethodID getKeyboardLayoutOverlay;
     jmethodID getDeviceAlias;
+    jmethodID getTouchCalibrationForInputDevice;
 } gServiceClassInfo;
 
 static struct {
@@ -105,6 +107,11 @@
     jmethodID constructor;
 } gInputDeviceIdentifierInfo;
 
+static struct {
+    jclass clazz;
+    jmethodID getAffineTransform;
+} gTouchCalibrationClassInfo;
+
 
 
 // --- Global functions ---
@@ -182,6 +189,7 @@
     void setSystemUiVisibility(int32_t visibility);
     void setPointerSpeed(int32_t speed);
     void setShowTouches(bool enabled);
+    void reloadCalibration();
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -190,6 +198,10 @@
     virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices);
     virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier);
     virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier);
+    virtual TouchAffineTransformation getTouchAffineTransformation(JNIEnv *env,
+            jfloatArray matrixArr);
+    virtual TouchAffineTransformation getTouchAffineTransformation(
+            const String8& inputDeviceDescriptor, int32_t surfaceRotation);
 
     /* --- InputDispatcherPolicyInterface implementation --- */
 
@@ -741,6 +753,11 @@
             InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
 }
 
+void NativeInputManager::reloadCalibration() {
+    mInputManager->getReader()->requestRefreshConfiguration(
+            InputReaderConfiguration::TOUCH_AFFINE_TRANSFORMATION);
+}
+
 bool NativeInputManager::isScreenOn() {
     return android_server_PowerManagerService_isScreenOn();
 }
@@ -749,6 +766,43 @@
     return android_server_PowerManagerService_isScreenBright();
 }
 
+TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
+        JNIEnv *env, jfloatArray matrixArr) {
+    ScopedFloatArrayRO matrix(env, matrixArr);
+    assert(matrix.size() == 6);
+
+    TouchAffineTransformation transform;
+    transform.x_scale  = matrix[0];
+    transform.x_ymix   = matrix[1];
+    transform.x_offset = matrix[2];
+    transform.y_xmix   = matrix[3];
+    transform.y_scale  = matrix[4];
+    transform.y_offset = matrix[5];
+
+    return transform;
+}
+
+TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
+        const String8& inputDeviceDescriptor, int32_t surfaceRotation) {
+    JNIEnv* env = jniEnv();
+
+    ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.string()));
+
+    jobject cal = env->CallObjectMethod(mServiceObj,
+            gServiceClassInfo.getTouchCalibrationForInputDevice, descriptorObj.get(),
+            surfaceRotation);
+
+    jfloatArray matrixArr = jfloatArray(env->CallObjectMethod(cal,
+            gTouchCalibrationClassInfo.getAffineTransform));
+
+    TouchAffineTransformation transform = getTouchAffineTransformation(env, matrixArr);
+
+    env->DeleteLocalRef(matrixArr);
+    env->DeleteLocalRef(cal);
+
+    return transform;
+}
+
 bool NativeInputManager::filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
     jobject inputEventObj;
 
@@ -1231,6 +1285,11 @@
     im->setShowTouches(enabled);
 }
 
+static void nativeReloadCalibration(JNIEnv* env, jclass clazz, jlong ptr) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+    im->reloadCalibration();
+}
+
 static void nativeVibrate(JNIEnv* env,
         jclass clazz, jlong ptr, jint deviceId, jlongArray patternObj,
         jint repeat, jint token) {
@@ -1336,6 +1395,8 @@
             (void*) nativeSetPointerSpeed },
     { "nativeSetShowTouches", "(JZ)V",
             (void*) nativeSetShowTouches },
+    { "nativeReloadCalibration", "(J)V",
+            (void*) nativeReloadCalibration },
     { "nativeVibrate", "(JI[JII)V",
             (void*) nativeVibrate },
     { "nativeCancelVibrate", "(JII)V",
@@ -1446,6 +1507,10 @@
     GET_METHOD_ID(gServiceClassInfo.getDeviceAlias, clazz,
             "getDeviceAlias", "(Ljava/lang/String;)Ljava/lang/String;");
 
+    GET_METHOD_ID(gServiceClassInfo.getTouchCalibrationForInputDevice, clazz,
+            "getTouchCalibrationForInputDevice",
+            "(Ljava/lang/String;I)Landroid/hardware/input/TouchCalibration;");
+
     // InputDevice
 
     FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
@@ -1468,6 +1533,14 @@
     GET_METHOD_ID(gInputDeviceIdentifierInfo.constructor, gInputDeviceIdentifierInfo.clazz,
             "<init>", "(Ljava/lang/String;II)V");
 
+    // TouchCalibration
+
+    FIND_CLASS(gTouchCalibrationClassInfo.clazz, "android/hardware/input/TouchCalibration");
+    gTouchCalibrationClassInfo.clazz = jclass(env->NewGlobalRef(gTouchCalibrationClassInfo.clazz));
+
+    GET_METHOD_ID(gTouchCalibrationClassInfo.getAffineTransform, gTouchCalibrationClassInfo.clazz,
+            "getAffineTransform", "()[F");
+
     return 0;
 }
 
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 00986d5..ac6585b 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -21,6 +21,7 @@
 
 namespace android {
 int register_android_server_AlarmManagerService(JNIEnv* env);
+int register_android_server_BatteryStatsService(JNIEnv* env);
 int register_android_server_ConsumerIrService(JNIEnv *env);
 int register_android_server_InputApplicationHandle(JNIEnv* env);
 int register_android_server_InputWindowHandle(JNIEnv* env);
@@ -69,6 +70,7 @@
     register_android_server_AssetAtlasService(env);
     register_android_server_ConsumerIrService(env);
     register_android_server_dreams_McuHal(env);
+    register_android_server_BatteryStatsService(env);
 
     return JNI_VERSION_1_4;
 }
diff --git a/tests/HwAccelerationTest/res/layout/isolation.xml b/tests/HwAccelerationTest/res/layout/isolation.xml
index 32eb628..e66db19 100644
--- a/tests/HwAccelerationTest/res/layout/isolation.xml
+++ b/tests/HwAccelerationTest/res/layout/isolation.xml
@@ -15,7 +15,6 @@
             android:layout_width="0dp"
             android:layout_height="200dp"
             android:layout_weight="1"
-            android:isolatedZVolume="false"
             android:orientation="vertical">
             <TextView style="@style/TopLeftReorderTextView"/>
             <TextView style="@style/BottomLeftReorderTextView"/>
@@ -25,7 +24,6 @@
             android:layout_height="200dp"
             android:layout_weight="1"
             android:translationY="50dp"
-            android:isolatedZVolume="false"
             android:orientation="vertical">
             <TextView style="@style/TopRightReorderTextView"/>
             <TextView style="@style/BottomRightReorderTextView"/>
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 2949f8e..806f8ff 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -5,7 +5,7 @@
 #
 
 # This tool is prebuilt if we're doing an app-only build.
-ifeq ($(TARGET_BUILD_APPS),)
+ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
 
 
 aapt_src_files := \
@@ -101,4 +101,4 @@
 include $(BUILD_EXECUTABLE)
 endif
 
-endif # TARGET_BUILD_APPS
+endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 8e856b7..44b8340 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -435,6 +435,7 @@
     LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
     PUBLIC_KEY_ATTR = 0x010103a6,
     CATEGORY_ATTR = 0x010103e8,
+    BANNER_ATTR = 0x10103f2,
 };
 
 String8 getComponentName(String8 &pkgName, String8 &componentName) {
@@ -762,6 +763,7 @@
             bool withinActivity = false;
             bool isMainActivity = false;
             bool isLauncherActivity = false;
+            bool isLeanbackLauncherActivity = false;
             bool isSearchable = false;
             bool withinApplication = false;
             bool withinSupportsInput = false;
@@ -872,6 +874,7 @@
             String8 activityName;
             String8 activityLabel;
             String8 activityIcon;
+            String8 activityBanner;
             String8 receiverName;
             String8 serviceName;
             Vector<String8> supportedInput;
@@ -896,16 +899,29 @@
                         withinApplication = false;
                         withinSupportsInput = false;
                     } else if (depth < 3) {
-                        if (withinActivity && isMainActivity && isLauncherActivity) {
+                        if (withinActivity && isMainActivity) {
                             String8 aName(getComponentName(pkg, activityName));
-                            printf("launchable-activity:");
-                            if (aName.length() > 0) {
-                                printf(" name='%s' ",
-                                        ResTable::normalizeForOutput(aName.string()).string());
+                            if (isLauncherActivity) {
+                                printf("launchable-activity:");
+                                if (aName.length() > 0) {
+                                    printf(" name='%s' ",
+                                            ResTable::normalizeForOutput(aName.string()).string());
+                                }
+                                printf(" label='%s' icon='%s'\n",
+                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
+                                        ResTable::normalizeForOutput(activityIcon.string()).string());
                             }
-                            printf(" label='%s' icon='%s'\n",
-                                    ResTable::normalizeForOutput(activityLabel.string()).string(),
-                                    ResTable::normalizeForOutput(activityIcon.string()).string());
+                            if (isLeanbackLauncherActivity) {
+                                printf("leanback-launchable-activity:");
+                                if (aName.length() > 0) {
+                                    printf(" name='%s' ",
+                                            ResTable::normalizeForOutput(aName.string()).string());
+                                }
+                                printf(" label='%s' icon='%s' banner='%s'\n",
+                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
+                                        ResTable::normalizeForOutput(activityIcon.string()).string(),
+                                        ResTable::normalizeForOutput(activityBanner.string()).string());
+                            }
                         }
                         if (!hasIntentFilter) {
                             hasOtherActivities |= withinActivity;
@@ -923,7 +939,7 @@
                         withinService = false;
                         withinReceiver = false;
                         hasIntentFilter = false;
-                        isMainActivity = isLauncherActivity = false;
+                        isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
                     } else if (depth < 4) {
                         if (withinIntentFilter) {
                             if (withinActivity) {
@@ -1329,6 +1345,13 @@
                                 goto bail;
                             }
 
+                            activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
+                            if (error != "") {
+                                fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
+                                        error.string());
+                                goto bail;
+                            }
+
                             int32_t orien = getResolvedIntegerAttribute(&res, tree,
                                     SCREEN_ORIENTATION_ATTR, &error);
                             if (error == "") {
@@ -1538,6 +1561,8 @@
                         if (withinActivity) {
                             if (category == "android.intent.category.LAUNCHER") {
                                 isLauncherActivity = true;
+                            } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
+                                isLeanbackLauncherActivity = true;
                             }
                         }
                     }
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index b1a548e..12f5b92 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -35,7 +35,9 @@
 // This holds an image as 8bpp RGBA.
 struct image_info
 {
-    image_info() : rows(NULL), is9Patch(false), allocRows(NULL) { }
+    image_info() : rows(NULL), is9Patch(false),
+        xDivs(NULL), yDivs(NULL), colors(NULL), allocRows(NULL) { }
+
     ~image_info() {
         if (rows && rows != allocRows) {
             free(rows);
@@ -46,9 +48,15 @@
             }
             free(allocRows);
         }
-        free(info9Patch.xDivs);
-        free(info9Patch.yDivs);
-        free(info9Patch.colors);
+        free(xDivs);
+        free(yDivs);
+        free(colors);
+    }
+
+    void* serialize9patch() {
+        void* serialized = Res_png_9patch::serialize(info9Patch, xDivs, yDivs, colors);
+        reinterpret_cast<Res_png_9patch*>(serialized)->deviceToFile();
+        return serialized;
     }
 
     png_uint_32 width;
@@ -58,6 +66,9 @@
     // 9-patch info.
     bool is9Patch;
     Res_png_9patch info9Patch;
+    int32_t* xDivs;
+    int32_t* yDivs;
+    uint32_t* colors;
 
     // Layout padding, if relevant
     bool haveLayoutBounds;
@@ -440,10 +451,10 @@
 {
     int left, right, top, bottom;
     select_patch(
-        hpatch, image->info9Patch.xDivs[0], image->info9Patch.xDivs[1],
+        hpatch, image->xDivs[0], image->xDivs[1],
         image->width, &left, &right);
     select_patch(
-        vpatch, image->info9Patch.yDivs[0], image->info9Patch.yDivs[1],
+        vpatch, image->yDivs[0], image->yDivs[1],
         image->height, &top, &bottom);
     //printf("Selecting h=%d v=%d: (%d,%d)-(%d,%d)\n",
     //       hpatch, vpatch, left, top, right, bottom);
@@ -462,10 +473,11 @@
 
     int maxSizeXDivs = W * sizeof(int32_t);
     int maxSizeYDivs = H * sizeof(int32_t);
-    int32_t* xDivs = image->info9Patch.xDivs = (int32_t*) malloc(maxSizeXDivs);
-    int32_t* yDivs = image->info9Patch.yDivs = (int32_t*) malloc(maxSizeYDivs);
-    uint8_t  numXDivs = 0;
-    uint8_t  numYDivs = 0;
+    int32_t* xDivs = image->xDivs = (int32_t*) malloc(maxSizeXDivs);
+    int32_t* yDivs = image->yDivs = (int32_t*) malloc(maxSizeYDivs);
+    uint8_t numXDivs = 0;
+    uint8_t numYDivs = 0;
+
     int8_t numColors;
     int numRows;
     int numCols;
@@ -618,7 +630,7 @@
 
     numColors = numRows * numCols;
     image->info9Patch.numColors = numColors;
-    image->info9Patch.colors = (uint32_t*)malloc(numColors * sizeof(uint32_t));
+    image->colors = (uint32_t*)malloc(numColors * sizeof(uint32_t));
 
     // Fill in color information for each patch.
 
@@ -661,7 +673,7 @@
                 right = xDivs[i];
             }
             c = get_color(image->rows, left, top, right - 1, bottom - 1);
-            image->info9Patch.colors[colorIndex++] = c;
+            image->colors[colorIndex++] = c;
             NOISY(if (c != Res_png_9patch::NO_COLOR) hasColor = true);
             left = right;
         }
@@ -673,14 +685,10 @@
     for (i=0; i<numColors; i++) {
         if (hasColor) {
             if (i == 0) printf("Colors in %s:\n ", imageName);
-            printf(" #%08x", image->info9Patch.colors[i]);
+            printf(" #%08x", image->colors[i]);
             if (i == numColors - 1) printf("\n");
         }
     }
-
-    image->is9Patch = true;
-    image->info9Patch.deviceToFile();
-
 getout:
     if (errorMsg) {
         fprintf(stderr,
@@ -700,14 +708,10 @@
     return NO_ERROR;
 }
 
-static void checkNinePatchSerialization(Res_png_9patch* inPatch,  void * data)
+static void checkNinePatchSerialization(Res_png_9patch* inPatch,  void* data)
 {
-    if (sizeof(void*) != sizeof(int32_t)) {
-        // can't deserialize on a non-32 bit system
-        return;
-    }
     size_t patchSize = inPatch->serializedSize();
-    void * newData = malloc(patchSize);
+    void* newData = malloc(patchSize);
     memcpy(newData, data, patchSize);
     Res_png_9patch* outPatch = inPatch->deserialize(newData);
     // deserialization is done in place, so outPatch == newData
@@ -730,34 +734,6 @@
     free(newData);
 }
 
-static bool patch_equals(Res_png_9patch& patch1, Res_png_9patch& patch2) {
-    if (!(patch1.numXDivs == patch2.numXDivs &&
-          patch1.numYDivs == patch2.numYDivs &&
-          patch1.numColors == patch2.numColors &&
-          patch1.paddingLeft == patch2.paddingLeft &&
-          patch1.paddingRight == patch2.paddingRight &&
-          patch1.paddingTop == patch2.paddingTop &&
-          patch1.paddingBottom == patch2.paddingBottom)) {
-            return false;
-    }
-    for (int i = 0; i < patch1.numColors; i++) {
-        if (patch1.colors[i] != patch2.colors[i]) {
-            return false;
-        }
-    }
-    for (int i = 0; i < patch1.numXDivs; i++) {
-        if (patch1.xDivs[i] != patch2.xDivs[i]) {
-            return false;
-        }
-    }
-    for (int i = 0; i < patch1.numYDivs; i++) {
-        if (patch1.yDivs[i] != patch2.yDivs[i]) {
-            return false;
-        }
-    }
-    return true;
-}
-
 static void dump_image(int w, int h, png_bytepp rows, int color_type)
 {
     int i, j, rr, gg, bb, aa;
@@ -1070,7 +1046,7 @@
                 : (png_byte*)"npTc";
         NOISY(printf("Adding 9-patch info...\n"));
         strcpy((char*)unknowns[p_index].name, "npTc");
-        unknowns[p_index].data = (png_byte*)imageInfo.info9Patch.serialize();
+        unknowns[p_index].data = (png_byte*)imageInfo.serialize9patch();
         unknowns[p_index].size = imageInfo.info9Patch.serializedSize();
         // TODO: remove the check below when everything works
         checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[p_index].data);
diff --git a/tools/aidl/Android.mk b/tools/aidl/Android.mk
index 77d46ab..efd60a2 100644
--- a/tools/aidl/Android.mk
+++ b/tools/aidl/Android.mk
@@ -3,7 +3,7 @@
 # Copies files into the directory structure described by a manifest
 
 # This tool is prebuilt if we're doing an app-only build.
-ifeq ($(TARGET_BUILD_APPS),)
+ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
 
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
@@ -26,4 +26,4 @@
 
 include $(BUILD_HOST_EXECUTABLE)
 
-endif # TARGET_BUILD_APPS
+endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK