Merge "Remove refSkPixelRef"
diff --git a/Android.mk b/Android.mk
index 4b0587f..db63739 100644
--- a/Android.mk
+++ b/Android.mk
@@ -299,6 +299,8 @@
 	core/java/android/view/IInputFilter.aidl \
 	core/java/android/view/IInputFilterHost.aidl \
 	core/java/android/view/IOnKeyguardExitResult.aidl \
+	core/java/android/view/IPinnedStackController.aidl \
+	core/java/android/view/IPinnedStackListener.aidl \
 	core/java/android/view/IRotationWatcher.aidl \
 	core/java/android/view/IWindow.aidl \
 	core/java/android/view/IWindowFocusObserver.aidl \
diff --git a/api/current.txt b/api/current.txt
index 4aa4229..47968de 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1041,6 +1041,7 @@
     field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
     field public static final int ringtoneType = 16843257; // 0x10101f9
     field public static final int rotation = 16843558; // 0x1010326
+    field public static final int rotationAnimation = 16843688; // 0x10103a8
     field public static final int rotationX = 16843559; // 0x1010327
     field public static final int rotationY = 16843560; // 0x1010328
     field public static final int roundIcon = 16844076; // 0x101052c
@@ -10061,7 +10062,7 @@
     field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
     field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
     field public static final int PROTECTION_MASK_BASE = 15; // 0xf
-    field public static final int PROTECTION_MASK_FLAGS = 4080; // 0xff0
+    field public static final int PROTECTION_MASK_FLAGS = 65520; // 0xfff0
     field public static final int PROTECTION_NORMAL = 0; // 0x0
     field public static final int PROTECTION_SIGNATURE = 2; // 0x2
     field public static final deprecated int PROTECTION_SIGNATURE_OR_SYSTEM = 3; // 0x3
@@ -11796,14 +11797,14 @@
     method public deprecated boolean clipRegion(android.graphics.Region);
     method public void concat(android.graphics.Matrix);
     method public void drawARGB(int, int, int, int);
-    method public void drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint);
     method public void drawArc(float, float, float, float, float, float, boolean, android.graphics.Paint);
+    method public void drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint);
     method public void drawBitmap(android.graphics.Bitmap, float, float, android.graphics.Paint);
-    method public void drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.RectF, android.graphics.Paint);
+    method public void drawBitmap(android.graphics.Bitmap, android.graphics.Matrix, android.graphics.Paint);
     method public void drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.Rect, android.graphics.Paint);
+    method public void drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.RectF, android.graphics.Paint);
     method public deprecated void drawBitmap(int[], int, int, float, float, int, int, boolean, android.graphics.Paint);
     method public deprecated void drawBitmap(int[], int, int, int, int, int, int, boolean, android.graphics.Paint);
-    method public void drawBitmap(android.graphics.Bitmap, android.graphics.Matrix, android.graphics.Paint);
     method public void drawBitmapMesh(android.graphics.Bitmap, int, int, float[], int, int[], int, android.graphics.Paint);
     method public void drawCircle(float, float, float, android.graphics.Paint);
     method public void drawColor(int);
@@ -11811,28 +11812,28 @@
     method public void drawLine(float, float, float, float, android.graphics.Paint);
     method public void drawLines(float[], int, int, android.graphics.Paint);
     method public void drawLines(float[], android.graphics.Paint);
-    method public void drawOval(android.graphics.RectF, android.graphics.Paint);
     method public void drawOval(float, float, float, float, android.graphics.Paint);
+    method public void drawOval(android.graphics.RectF, android.graphics.Paint);
     method public void drawPaint(android.graphics.Paint);
     method public void drawPath(android.graphics.Path, android.graphics.Paint);
     method public void drawPicture(android.graphics.Picture);
-    method public void drawPicture(android.graphics.Picture, android.graphics.RectF);
     method public void drawPicture(android.graphics.Picture, android.graphics.Rect);
+    method public void drawPicture(android.graphics.Picture, android.graphics.RectF);
     method public void drawPoint(float, float, android.graphics.Paint);
     method public void drawPoints(float[], int, int, android.graphics.Paint);
     method public void drawPoints(float[], android.graphics.Paint);
     method public deprecated void drawPosText(char[], int, int, float[], android.graphics.Paint);
     method public deprecated void drawPosText(java.lang.String, float[], android.graphics.Paint);
     method public void drawRGB(int, int, int);
-    method public void drawRect(android.graphics.RectF, android.graphics.Paint);
-    method public void drawRect(android.graphics.Rect, android.graphics.Paint);
     method public void drawRect(float, float, float, float, android.graphics.Paint);
-    method public void drawRoundRect(android.graphics.RectF, float, float, android.graphics.Paint);
+    method public void drawRect(android.graphics.Rect, android.graphics.Paint);
+    method public void drawRect(android.graphics.RectF, android.graphics.Paint);
     method public void drawRoundRect(float, float, float, float, float, float, android.graphics.Paint);
+    method public void drawRoundRect(android.graphics.RectF, float, float, android.graphics.Paint);
     method public void drawText(char[], int, int, float, float, android.graphics.Paint);
+    method public void drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint);
     method public void drawText(java.lang.String, float, float, android.graphics.Paint);
     method public void drawText(java.lang.String, int, int, float, float, android.graphics.Paint);
-    method public void drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint);
     method public void drawTextOnPath(char[], int, int, android.graphics.Path, float, float, android.graphics.Paint);
     method public void drawTextOnPath(java.lang.String, android.graphics.Path, float, float, android.graphics.Paint);
     method public void drawTextRun(char[], int, int, int, int, float, float, boolean, android.graphics.Paint);
diff --git a/api/system-current.txt b/api/system-current.txt
index 8ad1996..ff5e8735 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1147,6 +1147,7 @@
     field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
     field public static final int ringtoneType = 16843257; // 0x10101f9
     field public static final int rotation = 16843558; // 0x1010326
+    field public static final int rotationAnimation = 16843688; // 0x10103a8
     field public static final int rotationX = 16843559; // 0x1010327
     field public static final int rotationY = 16843560; // 0x1010328
     field public static final int roundIcon = 16844076; // 0x101052c
@@ -10486,6 +10487,7 @@
     field public static final int PROTECTION_DANGEROUS = 1; // 0x1
     field public static final int PROTECTION_FLAG_APPOP = 64; // 0x40
     field public static final int PROTECTION_FLAG_DEVELOPMENT = 32; // 0x20
+    field public static final int PROTECTION_FLAG_EPHEMERAL = 4096; // 0x1000
     field public static final int PROTECTION_FLAG_INSTALLER = 256; // 0x100
     field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
     field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
@@ -10494,7 +10496,7 @@
     field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
     field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
     field public static final int PROTECTION_MASK_BASE = 15; // 0xf
-    field public static final int PROTECTION_MASK_FLAGS = 4080; // 0xff0
+    field public static final int PROTECTION_MASK_FLAGS = 65520; // 0xfff0
     field public static final int PROTECTION_NORMAL = 0; // 0x0
     field public static final int PROTECTION_SIGNATURE = 2; // 0x2
     field public static final deprecated int PROTECTION_SIGNATURE_OR_SYSTEM = 3; // 0x3
@@ -12243,14 +12245,14 @@
     method public deprecated boolean clipRegion(android.graphics.Region);
     method public void concat(android.graphics.Matrix);
     method public void drawARGB(int, int, int, int);
-    method public void drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint);
     method public void drawArc(float, float, float, float, float, float, boolean, android.graphics.Paint);
+    method public void drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint);
     method public void drawBitmap(android.graphics.Bitmap, float, float, android.graphics.Paint);
-    method public void drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.RectF, android.graphics.Paint);
+    method public void drawBitmap(android.graphics.Bitmap, android.graphics.Matrix, android.graphics.Paint);
     method public void drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.Rect, android.graphics.Paint);
+    method public void drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.RectF, android.graphics.Paint);
     method public deprecated void drawBitmap(int[], int, int, float, float, int, int, boolean, android.graphics.Paint);
     method public deprecated void drawBitmap(int[], int, int, int, int, int, int, boolean, android.graphics.Paint);
-    method public void drawBitmap(android.graphics.Bitmap, android.graphics.Matrix, android.graphics.Paint);
     method public void drawBitmapMesh(android.graphics.Bitmap, int, int, float[], int, int[], int, android.graphics.Paint);
     method public void drawCircle(float, float, float, android.graphics.Paint);
     method public void drawColor(int);
@@ -12258,28 +12260,28 @@
     method public void drawLine(float, float, float, float, android.graphics.Paint);
     method public void drawLines(float[], int, int, android.graphics.Paint);
     method public void drawLines(float[], android.graphics.Paint);
-    method public void drawOval(android.graphics.RectF, android.graphics.Paint);
     method public void drawOval(float, float, float, float, android.graphics.Paint);
+    method public void drawOval(android.graphics.RectF, android.graphics.Paint);
     method public void drawPaint(android.graphics.Paint);
     method public void drawPath(android.graphics.Path, android.graphics.Paint);
     method public void drawPicture(android.graphics.Picture);
-    method public void drawPicture(android.graphics.Picture, android.graphics.RectF);
     method public void drawPicture(android.graphics.Picture, android.graphics.Rect);
+    method public void drawPicture(android.graphics.Picture, android.graphics.RectF);
     method public void drawPoint(float, float, android.graphics.Paint);
     method public void drawPoints(float[], int, int, android.graphics.Paint);
     method public void drawPoints(float[], android.graphics.Paint);
     method public deprecated void drawPosText(char[], int, int, float[], android.graphics.Paint);
     method public deprecated void drawPosText(java.lang.String, float[], android.graphics.Paint);
     method public void drawRGB(int, int, int);
-    method public void drawRect(android.graphics.RectF, android.graphics.Paint);
-    method public void drawRect(android.graphics.Rect, android.graphics.Paint);
     method public void drawRect(float, float, float, float, android.graphics.Paint);
-    method public void drawRoundRect(android.graphics.RectF, float, float, android.graphics.Paint);
+    method public void drawRect(android.graphics.Rect, android.graphics.Paint);
+    method public void drawRect(android.graphics.RectF, android.graphics.Paint);
     method public void drawRoundRect(float, float, float, float, float, float, android.graphics.Paint);
+    method public void drawRoundRect(android.graphics.RectF, float, float, android.graphics.Paint);
     method public void drawText(char[], int, int, float, float, android.graphics.Paint);
+    method public void drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint);
     method public void drawText(java.lang.String, float, float, android.graphics.Paint);
     method public void drawText(java.lang.String, int, int, float, float, android.graphics.Paint);
-    method public void drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint);
     method public void drawTextOnPath(char[], int, int, android.graphics.Path, float, float, android.graphics.Paint);
     method public void drawTextOnPath(java.lang.String, android.graphics.Path, float, float, android.graphics.Paint);
     method public void drawTextRun(char[], int, int, int, int, float, float, boolean, android.graphics.Paint);
diff --git a/api/test-current.txt b/api/test-current.txt
index 7a32539..907f9a4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1041,6 +1041,7 @@
     field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
     field public static final int ringtoneType = 16843257; // 0x10101f9
     field public static final int rotation = 16843558; // 0x1010326
+    field public static final int rotationAnimation = 16843688; // 0x10103a8
     field public static final int rotationX = 16843559; // 0x1010327
     field public static final int rotationY = 16843560; // 0x1010328
     field public static final int roundIcon = 16844076; // 0x101052c
@@ -10069,6 +10070,7 @@
     field public static final int PROTECTION_DANGEROUS = 1; // 0x1
     field public static final int PROTECTION_FLAG_APPOP = 64; // 0x40
     field public static final int PROTECTION_FLAG_DEVELOPMENT = 32; // 0x20
+    field public static final int PROTECTION_FLAG_EPHEMERAL = 4096; // 0x1000
     field public static final int PROTECTION_FLAG_INSTALLER = 256; // 0x100
     field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
     field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
@@ -10077,7 +10079,7 @@
     field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
     field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
     field public static final int PROTECTION_MASK_BASE = 15; // 0xf
-    field public static final int PROTECTION_MASK_FLAGS = 4080; // 0xff0
+    field public static final int PROTECTION_MASK_FLAGS = 65520; // 0xfff0
     field public static final int PROTECTION_NORMAL = 0; // 0x0
     field public static final int PROTECTION_SIGNATURE = 2; // 0x2
     field public static final deprecated int PROTECTION_SIGNATURE_OR_SYSTEM = 3; // 0x3
@@ -11813,14 +11815,14 @@
     method public deprecated boolean clipRegion(android.graphics.Region);
     method public void concat(android.graphics.Matrix);
     method public void drawARGB(int, int, int, int);
-    method public void drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint);
     method public void drawArc(float, float, float, float, float, float, boolean, android.graphics.Paint);
+    method public void drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint);
     method public void drawBitmap(android.graphics.Bitmap, float, float, android.graphics.Paint);
-    method public void drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.RectF, android.graphics.Paint);
+    method public void drawBitmap(android.graphics.Bitmap, android.graphics.Matrix, android.graphics.Paint);
     method public void drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.Rect, android.graphics.Paint);
+    method public void drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.RectF, android.graphics.Paint);
     method public deprecated void drawBitmap(int[], int, int, float, float, int, int, boolean, android.graphics.Paint);
     method public deprecated void drawBitmap(int[], int, int, int, int, int, int, boolean, android.graphics.Paint);
-    method public void drawBitmap(android.graphics.Bitmap, android.graphics.Matrix, android.graphics.Paint);
     method public void drawBitmapMesh(android.graphics.Bitmap, int, int, float[], int, int[], int, android.graphics.Paint);
     method public void drawCircle(float, float, float, android.graphics.Paint);
     method public void drawColor(int);
@@ -11828,28 +11830,28 @@
     method public void drawLine(float, float, float, float, android.graphics.Paint);
     method public void drawLines(float[], int, int, android.graphics.Paint);
     method public void drawLines(float[], android.graphics.Paint);
-    method public void drawOval(android.graphics.RectF, android.graphics.Paint);
     method public void drawOval(float, float, float, float, android.graphics.Paint);
+    method public void drawOval(android.graphics.RectF, android.graphics.Paint);
     method public void drawPaint(android.graphics.Paint);
     method public void drawPath(android.graphics.Path, android.graphics.Paint);
     method public void drawPicture(android.graphics.Picture);
-    method public void drawPicture(android.graphics.Picture, android.graphics.RectF);
     method public void drawPicture(android.graphics.Picture, android.graphics.Rect);
+    method public void drawPicture(android.graphics.Picture, android.graphics.RectF);
     method public void drawPoint(float, float, android.graphics.Paint);
     method public void drawPoints(float[], int, int, android.graphics.Paint);
     method public void drawPoints(float[], android.graphics.Paint);
     method public deprecated void drawPosText(char[], int, int, float[], android.graphics.Paint);
     method public deprecated void drawPosText(java.lang.String, float[], android.graphics.Paint);
     method public void drawRGB(int, int, int);
-    method public void drawRect(android.graphics.RectF, android.graphics.Paint);
-    method public void drawRect(android.graphics.Rect, android.graphics.Paint);
     method public void drawRect(float, float, float, float, android.graphics.Paint);
-    method public void drawRoundRect(android.graphics.RectF, float, float, android.graphics.Paint);
+    method public void drawRect(android.graphics.Rect, android.graphics.Paint);
+    method public void drawRect(android.graphics.RectF, android.graphics.Paint);
     method public void drawRoundRect(float, float, float, float, float, float, android.graphics.Paint);
+    method public void drawRoundRect(android.graphics.RectF, float, float, android.graphics.Paint);
     method public void drawText(char[], int, int, float, float, android.graphics.Paint);
+    method public void drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint);
     method public void drawText(java.lang.String, float, float, android.graphics.Paint);
     method public void drawText(java.lang.String, int, int, float, float, android.graphics.Paint);
-    method public void drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint);
     method public void drawTextOnPath(char[], int, int, android.graphics.Path, float, float, android.graphics.Paint);
     method public void drawTextOnPath(java.lang.String, android.graphics.Path, float, float, android.graphics.Paint);
     method public void drawTextRun(char[], int, int, int, int, float, float, boolean, android.graphics.Paint);
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 3c2efd8..7967e2a 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -74,6 +74,7 @@
 static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/system/time/last_time_change";
 static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate";
 static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/system/time/time_is_accurate";
+static const char TIME_FORMAT_12_HOUR_FLAG_FILE_PATH[] = "/data/system/time/time_format_12_hour";
 // Java timestamp format. Don't show the clock if the date is before 2000-01-01 00:00:00.
 static const long long ACCURATE_TIME_EPOCH = 946684800000;
 static constexpr char FONT_BEGIN_CHAR = ' ';
@@ -99,7 +100,7 @@
 // ---------------------------------------------------------------------------
 
 BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
-        mTimeCheckThread(NULL) {
+        mTimeFormat12Hour(false), mTimeCheckThread(NULL) {
     mSession = new SurfaceComposerClient();
 
     // If the system has already booted, the animation is not being used for a boot.
@@ -587,9 +588,10 @@
     glBindTexture(GL_TEXTURE_2D, 0);
 }
 
-// We render 24 hour time.
+// We render 12 or 24 hour time.
 void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos) {
-    static constexpr char TIME_FORMAT[] = "%H:%M";
+    static constexpr char TIME_FORMAT_12[] = "%l:%M";
+    static constexpr char TIME_FORMAT_24[] = "%H:%M";
     static constexpr int TIME_LENGTH = 6;
 
     time_t rawtime;
@@ -597,7 +599,8 @@
     struct tm* timeInfo = localtime(&rawtime);
 
     char timeBuff[TIME_LENGTH];
-    size_t length = strftime(timeBuff, TIME_LENGTH, TIME_FORMAT, timeInfo);
+    const char* timeFormat = mTimeFormat12Hour ? TIME_FORMAT_12 : TIME_FORMAT_24;
+    size_t length = strftime(timeBuff, TIME_LENGTH, timeFormat, timeInfo);
 
     if (length != TIME_LENGTH - 1) {
         ALOGE("Couldn't format time; abandoning boot animation clock");
@@ -1059,6 +1062,11 @@
     }
 
     struct stat statResult;
+
+    if(stat(TIME_FORMAT_12_HOUR_FLAG_FILE_PATH, &statResult) == 0) {
+        mTimeFormat12Hour = true;
+    }
+
     if(stat(ACCURATE_TIME_FLAG_FILE_PATH, &statResult) == 0) {
         mTimeIsAccurate = true;
         return true;
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 42759f1..7a2e4c2 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -152,6 +152,7 @@
     sp<Surface> mFlingerSurface;
     bool        mClockEnabled;
     bool        mTimeIsAccurate;
+    bool        mTimeFormat12Hour;
     bool        mSystemBoot;
     String8     mZipFileName;
     SortedVector<String8> mLoadedFiles;
diff --git a/core/java/android/app/ActivityManager.aidl b/core/java/android/app/ActivityManager.aidl
index 92350da..fa6481b 100644
--- a/core/java/android/app/ActivityManager.aidl
+++ b/core/java/android/app/ActivityManager.aidl
@@ -17,3 +17,4 @@
 package android.app;
 
 parcelable ActivityManager.RecentTaskInfo;
+parcelable ActivityManager.TaskDescription;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a2f7aea..623a11d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -964,6 +964,14 @@
             return true;
         }
 
+        case UNREGISTER_TASK_STACK_LISTENER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            unregisterTaskStackListener(ITaskStackListener.Stub.asInterface(token));
+            reply.writeNoException();
+            return true;
+        }
+
         case GET_TASK_FOR_ACTIVITY_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
@@ -2932,22 +2940,6 @@
             reply.writeNoException();
             return true;
         }
-        case GET_DEFAULT_PICTURE_IN_PICTURE_BOUNDS_TRANSACTION: {
-            data.enforceInterface(IActivityManager.descriptor);
-            final int displayId = data.readInt();
-            Rect r = getDefaultPictureInPictureBounds(displayId);
-            reply.writeNoException();
-            r.writeToParcel(reply, 0);
-            return true;
-        }
-        case GET_PICTURE_IN_PICTURE_MOVEMENT_BOUNDS_TRANSACTION: {
-            data.enforceInterface(IActivityManager.descriptor);
-            final int displayId = data.readInt();
-            Rect r = getPictureInPictureMovementBounds(displayId);
-            reply.writeNoException();
-            r.writeToParcel(reply, 0);
-            return true;
-        }
         case SET_VR_MODE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             final IBinder token = data.readStrongBinder();
@@ -4208,6 +4200,18 @@
         data.recycle();
         reply.recycle();
     }
+    @Override
+    public void unregisterTaskStackListener(ITaskStackListener listener) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(listener.asBinder());
+        mRemote.transact(UNREGISTER_TASK_STACK_LISTENER_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
     public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -7007,36 +7011,6 @@
     }
 
     @Override
-    public Rect getDefaultPictureInPictureBounds(int displayId) throws RemoteException
-    {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken(IActivityManager.descriptor);
-        data.writeInt(displayId);
-        mRemote.transact(GET_DEFAULT_PICTURE_IN_PICTURE_BOUNDS_TRANSACTION, data, reply, 0);
-        reply.readException();
-        Rect rect = Rect.CREATOR.createFromParcel(reply);
-        data.recycle();
-        reply.recycle();
-        return rect;
-    }
-
-    @Override
-    public Rect getPictureInPictureMovementBounds(int displayId) throws RemoteException
-    {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken(IActivityManager.descriptor);
-        data.writeInt(displayId);
-        mRemote.transact(GET_PICTURE_IN_PICTURE_MOVEMENT_BOUNDS_TRANSACTION, data, reply, 0);
-        reply.readException();
-        Rect rect = Rect.CREATOR.createFromParcel(reply);
-        data.recycle();
-        reply.recycle();
-        return rect;
-    }
-
-    @Override
     public boolean isAppForeground(int uid) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 3670c58..7b25c76 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -214,6 +214,7 @@
     public int getFocusedStackId() throws RemoteException;
     public void setFocusedTask(int taskId) throws RemoteException;
     public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException;
+    public void unregisterTaskStackListener(ITaskStackListener listener) throws RemoteException;
     public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
     public ContentProviderHolder getContentProvider(IApplicationThread caller,
             String name, int userId, boolean stable) throws RemoteException;
@@ -657,16 +658,6 @@
 
     public void enterPictureInPictureMode(IBinder token) throws RemoteException;
 
-    /**
-     * @return the default bounds of the PIP on the default display.
-     */
-    public Rect getDefaultPictureInPictureBounds(int displayId) throws RemoteException;
-
-    /**
-     * @return the movement bounds of the PIP on the default display.
-     */
-    public Rect getPictureInPictureMovementBounds(int displayId) throws RemoteException;
-
     public int setVrMode(IBinder token, boolean enabled, ComponentName packageName)
             throws RemoteException;
 
@@ -1111,7 +1102,6 @@
 
     // Start of O transactions
     int REQUEST_ACTIVITY_RELAUNCH = IBinder.FIRST_CALL_TRANSACTION+400;
-    int GET_DEFAULT_PICTURE_IN_PICTURE_BOUNDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 401;
-    int GET_PICTURE_IN_PICTURE_MOVEMENT_BOUNDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 402;
-    int UPDATE_DISPLAY_OVERRIDE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 403;
+    int UPDATE_DISPLAY_OVERRIDE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 401;
+    int UNREGISTER_TASK_STACK_LISTENER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+402;
 }
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 496ca6f..e454ae1 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -16,6 +16,9 @@
 
 package android.app;
 
+import android.app.ActivityManager;
+import android.content.ComponentName;
+
 /** @hide */
 oneway interface ITaskStackListener {
     /** Called whenever there are changes to the state of tasks in a stack. */
@@ -45,4 +48,51 @@
      * Callen when we launched an activity that is dismissed the docked stack.
      */
     void onActivityDismissingDockedStack();
+
+    /**
+     * Called when a task is added.
+     *
+     * @param taskId id of the task.
+     * @param componentName of the activity that the task is being started with.
+    */
+    void onTaskCreated(int taskId, in ComponentName componentName);
+
+    /**
+     * Called when a task is removed.
+     *
+     * @param taskId id of the task.
+    */
+    void onTaskRemoved(int taskId);
+
+    /**
+     * Called when a task is moved to the front of its stack.
+     *
+     * @param taskId id of the task.
+    */
+    void onTaskMovedToFront(int taskId);
+
+    /**
+     * Called when a task’s description is changed due to an activity calling
+     * ActivityManagerService.setTaskDescription
+     *
+     * @param taskId id of the task.
+     * @param td the new TaskDescription.
+    */
+    void onTaskDescriptionChanged(int taskId, in ActivityManager.TaskDescription td);
+
+    /**
+     * Called when a activity’s orientation is changed due to it calling
+     * ActivityManagerService.setRequestedOrientation
+     *
+     * @param taskId id of the task that the activity is in.
+     * @param requestedOrientation the new requested orientation.
+    */
+    void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation);
+
+    /**
+     * Called when the task is about to be finished but before its surfaces are
+     * removed from the window manager. This allows interested parties to
+     * perform relevant animations before the window disappears.
+     */
+    void onTaskRemovalStarted(int taskId);
 }
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
new file mode 100644
index 0000000..0639552
--- /dev/null
+++ b/core/java/android/app/TaskStackListener.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 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.app;
+
+import android.content.ComponentName;
+import android.os.RemoteException;
+
+/**
+ * Classes interested in observing only a subset of changes using ITaskStackListener can extend
+ * this class to avoid having to implement all the methods.
+ * @hide
+ */
+public abstract class TaskStackListener extends ITaskStackListener.Stub {
+    @Override
+    public void onTaskStackChanged() throws RemoteException {
+    }
+
+    @Override
+    public void onActivityPinned() throws RemoteException {
+    }
+
+    @Override
+    public void onPinnedActivityRestartAttempt() throws RemoteException {
+    }
+
+    @Override
+    public void onPinnedStackAnimationEnded() throws RemoteException {
+    }
+
+    @Override
+    public void onActivityForcedResizable(String packageName, int taskId) throws RemoteException {
+    }
+
+    @Override
+    public void onActivityDismissingDockedStack() throws RemoteException {
+    }
+
+    @Override
+    public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
+    }
+
+    @Override
+    public void onTaskRemoved(int taskId) throws RemoteException {
+    }
+
+    @Override
+    public void onTaskMovedToFront(int taskId) throws RemoteException {
+    }
+
+    @Override
+    public void onTaskRemovalStarted(int taskId) {
+    }
+
+    @Override
+    public void onTaskDescriptionChanged(int taskId, ActivityManager.TaskDescription td)
+            throws RemoteException {
+    }
+
+    @Override
+    public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation)
+            throws RemoteException {
+    }
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9ce9dec..118e1f3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1095,7 +1095,7 @@
      * names.  If there are no administrators {@code null} may be
      * returned.
      */
-    public List<ComponentName> getActiveAdmins() {
+    public @Nullable List<ComponentName> getActiveAdmins() {
         throwIfParentInstance("getActiveAdmins");
         return getActiveAdminsAsUser(myUserId());
     }
@@ -1104,7 +1104,7 @@
      * @see #getActiveAdmins()
      * @hide
      */
-    public List<ComponentName> getActiveAdminsAsUser(int userId) {
+    public @Nullable List<ComponentName> getActiveAdminsAsUser(int userId) {
         if (mService != null) {
             try {
                 return mService.getActiveAdmins(userId);
@@ -2500,7 +2500,7 @@
      *            of the device admin that sets the proxy.
      * @hide
      */
-    public ComponentName setGlobalProxy(@NonNull ComponentName admin, Proxy proxySpec,
+    public @Nullable ComponentName setGlobalProxy(@NonNull ComponentName admin, Proxy proxySpec,
             List<String> exclusionList ) {
         throwIfParentInstance("setGlobalProxy");
         if (proxySpec == null) {
@@ -2584,7 +2584,7 @@
      *         if no admin has set the proxy.
      * @hide
      */
-    public ComponentName getGlobalProxyAdmin() {
+    public @Nullable ComponentName getGlobalProxyAdmin() {
         if (mService != null) {
             try {
                 return mService.getGlobalProxyAdmin(myUserId());
@@ -2894,8 +2894,8 @@
      * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
      *         owner.
      */
-    public List<byte[]> getInstalledCaCerts(@Nullable ComponentName admin) {
-        List<byte[]> certs = new ArrayList<byte[]>();
+    public @NonNull List<byte[]> getInstalledCaCerts(@Nullable ComponentName admin) {
+        final List<byte[]> certs = new ArrayList<byte[]>();
         throwIfParentInstance("getInstalledCaCerts");
         if (mService != null) {
             try {
@@ -3108,7 +3108,8 @@
      *         none is set.
      * @throws SecurityException if {@code admin} is not a device or a profile owner.
      */
-    public String getCertInstallerPackage(@NonNull ComponentName admin) throws SecurityException {
+    public @Nullable String getCertInstallerPackage(@NonNull ComponentName admin)
+            throws SecurityException {
         throwIfParentInstance("getCertInstallerPackage");
         if (mService != null) {
             try {
@@ -3177,7 +3178,7 @@
      *         is set.
      * @throws SecurityException if {@code admin} is not a device or a profile owner.
      */
-    public String getAlwaysOnVpnPackage(@NonNull ComponentName admin) {
+    public @Nullable String getAlwaysOnVpnPackage(@NonNull ComponentName admin) {
         throwIfParentInstance("getAlwaysOnVpnPackage");
         if (mService != null) {
             try {
@@ -3798,7 +3799,7 @@
      * @hide
      */
     @SystemApi
-    public String getDeviceOwner() {
+    public @Nullable String getDeviceOwner() {
         throwIfParentInstance("getDeviceOwner");
         final ComponentName name = getDeviceOwnerComponentOnCallingUser();
         return name != null ? name.getPackageName() : null;
@@ -3843,7 +3844,7 @@
      */
     @Deprecated
     @SystemApi
-    public String getDeviceInitializerApp() {
+    public @Nullable String getDeviceInitializerApp() {
         return null;
     }
 
@@ -3854,7 +3855,7 @@
      */
     @Deprecated
     @SystemApi
-    public ComponentName getDeviceInitializerComponent() {
+    public @Nullable ComponentName getDeviceInitializerComponent() {
         return null;
     }
 
@@ -4020,8 +4021,8 @@
      *         this method.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
-    public String[] setPackagesSuspended(@NonNull ComponentName admin, String[] packageNames,
-            boolean suspended) {
+    public @NonNull String[] setPackagesSuspended(@NonNull ComponentName admin,
+            @NonNull String[] packageNames, boolean suspended) {
         throwIfParentInstance("setPackagesSuspended");
         if (mService != null) {
             try {
@@ -4128,7 +4129,7 @@
      * @throws IllegalArgumentException if the userId is invalid.
      */
     @SystemApi
-    public ComponentName getProfileOwner() throws IllegalArgumentException {
+    public @Nullable ComponentName getProfileOwner() throws IllegalArgumentException {
         throwIfParentInstance("getProfileOwner");
         return getProfileOwnerAsUser(Process.myUserHandle().getIdentifier());
     }
@@ -4137,7 +4138,8 @@
      * @see #getProfileOwner()
      * @hide
      */
-    public ComponentName getProfileOwnerAsUser(final int userId) throws IllegalArgumentException {
+    public @Nullable ComponentName getProfileOwnerAsUser(final int userId)
+            throws IllegalArgumentException {
         if (mService != null) {
             try {
                 return mService.getProfileOwner(userId);
@@ -4154,7 +4156,7 @@
      *         if one is not set.
      * @throws IllegalArgumentException if the userId is invalid.
      */
-    public String getProfileOwnerName() throws IllegalArgumentException {
+    public @Nullable String getProfileOwnerName() throws IllegalArgumentException {
         if (mService != null) {
             try {
                 return mService.getProfileOwnerName(Process.myUserHandle().getIdentifier());
@@ -4173,7 +4175,7 @@
      * @throws IllegalArgumentException if the userId is invalid.
      */
     @SystemApi
-    public String getProfileOwnerNameAsUser(int userId) throws IllegalArgumentException {
+    public @Nullable String getProfileOwnerNameAsUser(int userId) throws IllegalArgumentException {
         throwIfParentInstance("getProfileOwnerNameAsUser");
         if (mService != null) {
             try {
@@ -4278,7 +4280,8 @@
      *         {@code null} if none is set.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
-    public String getApplicationRestrictionsManagingPackage(@NonNull ComponentName admin) {
+    public @Nullable String getApplicationRestrictionsManagingPackage(
+            @NonNull ComponentName admin) {
         throwIfParentInstance("getApplicationRestrictionsManagingPackage");
         if (mService != null) {
             try {
@@ -4416,14 +4419,14 @@
      * @param agent Which component to get enabled features for.
      * @return configuration for the given trust agent.
      */
-    public List<PersistableBundle> getTrustAgentConfiguration(@Nullable ComponentName admin,
-            @NonNull ComponentName agent) {
+    public @Nullable List<PersistableBundle> getTrustAgentConfiguration(
+            @Nullable ComponentName admin, @NonNull ComponentName agent) {
         return getTrustAgentConfiguration(admin, agent, myUserId());
     }
 
     /** @hide per-user version */
-    public List<PersistableBundle> getTrustAgentConfiguration(@Nullable ComponentName admin,
-            @NonNull ComponentName agent, int userHandle) {
+    public @Nullable List<PersistableBundle> getTrustAgentConfiguration(
+            @Nullable ComponentName admin, @NonNull ComponentName agent, int userHandle) {
         if (mService != null) {
             try {
                 return mService.getTrustAgentConfiguration(admin, agent, userHandle,
@@ -4738,7 +4741,7 @@
      * @return List of accessiblity service package names.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
-    public List<String> getPermittedAccessibilityServices(@NonNull ComponentName admin) {
+    public @Nullable List<String> getPermittedAccessibilityServices(@NonNull ComponentName admin) {
         throwIfParentInstance("getPermittedAccessibilityServices");
         if (mService != null) {
             try {
@@ -4786,7 +4789,7 @@
      * @hide
      */
      @SystemApi
-     public List<String> getPermittedAccessibilityServices(int userId) {
+     public @Nullable List<String> getPermittedAccessibilityServices(int userId) {
         throwIfParentInstance("getPermittedAccessibilityServices");
         if (mService != null) {
             try {
@@ -4840,7 +4843,7 @@
      * @return List of input method package names.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
-    public List<String> getPermittedInputMethods(@NonNull ComponentName admin) {
+    public @Nullable List<String> getPermittedInputMethods(@NonNull ComponentName admin) {
         throwIfParentInstance("getPermittedInputMethods");
         if (mService != null) {
             try {
@@ -4886,7 +4889,7 @@
      * @hide
      */
     @SystemApi
-    public List<String> getPermittedInputMethodsForCurrentUser() {
+    public @Nullable List<String> getPermittedInputMethodsForCurrentUser() {
         throwIfParentInstance("getPermittedInputMethodsForCurrentUser");
         if (mService != null) {
             try {
@@ -4907,7 +4910,7 @@
      * @return List of package names to keep cached.
      * @hide
      */
-    public List<String> getKeepUninstalledPackages(@NonNull ComponentName admin) {
+    public @Nullable List<String> getKeepUninstalledPackages(@NonNull ComponentName admin) {
         throwIfParentInstance("getKeepUninstalledPackages");
         if (mService != null) {
             try {
@@ -4959,7 +4962,7 @@
      * @removed From {@link android.os.Build.VERSION_CODES#N}
      */
     @Deprecated
-    public UserHandle createUser(@NonNull ComponentName admin, String name) {
+    public @Nullable UserHandle createUser(@NonNull ComponentName admin, String name) {
         return null;
     }
 
@@ -4992,7 +4995,7 @@
      * @removed From {@link android.os.Build.VERSION_CODES#N}
      */
     @Deprecated
-    public UserHandle createAndInitializeUser(@NonNull ComponentName admin, String name,
+    public @Nullable UserHandle createAndInitializeUser(@NonNull ComponentName admin, String name,
             String ownerName, @NonNull ComponentName profileOwnerComponent, Bundle adminExtras) {
         return null;
     }
@@ -5037,7 +5040,8 @@
      *         user could not be created.
      * @throws SecurityException if {@code admin} is not a device owner.
      */
-    public UserHandle createAndManageUser(@NonNull ComponentName admin, @NonNull String name,
+    public @Nullable UserHandle createAndManageUser(@NonNull ComponentName admin,
+            @NonNull String name,
             @NonNull ComponentName profileOwner, @Nullable PersistableBundle adminExtras,
             int flags) {
         throwIfParentInstance("createAndManageUser");
@@ -5104,7 +5108,8 @@
      * @see {@link #setApplicationRestrictionsManagingPackage}
      */
     @WorkerThread
-    public Bundle getApplicationRestrictions(@Nullable ComponentName admin, String packageName) {
+    public @NonNull Bundle getApplicationRestrictions(
+            @Nullable ComponentName admin, String packageName) {
         throwIfParentInstance("getApplicationRestrictions");
         if (mService != null) {
             try {
@@ -5171,7 +5176,7 @@
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
-    public Bundle getUserRestrictions(@NonNull ComponentName admin) {
+    public @NonNull Bundle getUserRestrictions(@NonNull ComponentName admin) {
         throwIfParentInstance("getUserRestrictions");
         Bundle ret = null;
         if (mService != null) {
@@ -5311,7 +5316,7 @@
      *
      * @see #setAccountManagementDisabled
      */
-    public String[] getAccountTypesWithManagementDisabled() {
+    public @Nullable String[] getAccountTypesWithManagementDisabled() {
         throwIfParentInstance("getAccountTypesWithManagementDisabled");
         return getAccountTypesWithManagementDisabledAsUser(myUserId());
     }
@@ -5320,7 +5325,7 @@
      * @see #getAccountTypesWithManagementDisabled()
      * @hide
      */
-    public String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
+    public @Nullable String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
         if (mService != null) {
             try {
                 return mService.getAccountTypesWithManagementDisabledAsUser(userId);
@@ -5366,7 +5371,7 @@
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @hide
      */
-    public String[] getLockTaskPackages(@NonNull ComponentName admin) {
+    public @NonNull String[] getLockTaskPackages(@NonNull ComponentName admin) {
         throwIfParentInstance("getLockTaskPackages");
         if (mService != null) {
             try {
@@ -5651,7 +5656,7 @@
      * @see #removeCrossProfileWidgetProvider(android.content.ComponentName, String)
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
-    public List<String> getCrossProfileWidgetProviders(@NonNull ComponentName admin) {
+    public @NonNull List<String> getCrossProfileWidgetProviders(@NonNull ComponentName admin) {
         throwIfParentInstance("getCrossProfileWidgetProviders");
         if (mService != null) {
             try {
@@ -5709,7 +5714,7 @@
      *
      * @return The current policy object, or {@code null} if no policy is set.
      */
-    public SystemUpdatePolicy getSystemUpdatePolicy() {
+    public @Nullable SystemUpdatePolicy getSystemUpdatePolicy() {
         throwIfParentInstance("getSystemUpdatePolicy");
         if (mService != null) {
             try {
@@ -5948,7 +5953,7 @@
      *         The address will be in the {@code XX:XX:XX:XX:XX:XX} format.
      * @throws SecurityException if {@code admin} is not a device owner.
      */
-    public String getWifiMacAddress(@NonNull ComponentName admin) {
+    public @Nullable String getWifiMacAddress(@NonNull ComponentName admin) {
         throwIfParentInstance("getWifiMacAddress");
         try {
             return mService.getWifiMacAddress(admin);
@@ -6057,7 +6062,7 @@
      *         null if no message has been set.
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
-    public CharSequence getLongSupportMessage(@NonNull ComponentName admin) {
+    public @Nullable CharSequence getLongSupportMessage(@NonNull ComponentName admin) {
         throwIfParentInstance("getLongSupportMessage");
         if (mService != null) {
             try {
@@ -6078,7 +6083,7 @@
      *
      * @hide
      */
-    public CharSequence getShortSupportMessageForUser(@NonNull ComponentName admin,
+    public @Nullable CharSequence getShortSupportMessageForUser(@NonNull ComponentName admin,
             int userHandle) {
         if (mService != null) {
             try {
@@ -6100,7 +6105,8 @@
      *
      * @hide
      */
-    public CharSequence getLongSupportMessageForUser(@NonNull ComponentName admin, int userHandle) {
+    public @Nullable CharSequence getLongSupportMessageForUser(
+            @NonNull ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
                 return mService.getLongSupportMessageForUser(admin, userHandle);
@@ -6155,7 +6161,7 @@
      * @return a new instance of {@link DevicePolicyManager} that acts on the parent profile.
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
-    public DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
+    public @NonNull DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
         throwIfParentInstance("getParentProfileInstance");
         try {
             if (!mService.isManagedProfile(admin)) {
@@ -6225,7 +6231,7 @@
      * or {@code null} if rate limitation is exceeded or if logging is currently disabled.
      * @throws SecurityException if {@code admin} is not a device owner.
      */
-    public List<SecurityEvent> retrieveSecurityLogs(@NonNull ComponentName admin) {
+    public @Nullable List<SecurityEvent> retrieveSecurityLogs(@NonNull ComponentName admin) {
         throwIfParentInstance("retrieveSecurityLogs");
         try {
             ParceledListSlice<SecurityEvent> list = mService.retrieveSecurityLogs(admin);
@@ -6246,7 +6252,7 @@
      *
      * @hide
      */
-    public DevicePolicyManager getParentProfileInstance(UserInfo uInfo) {
+    public @NonNull DevicePolicyManager getParentProfileInstance(UserInfo uInfo) {
         mContext.checkSelfPermission(
                 android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         if (!uInfo.isManagedProfile()) {
@@ -6272,7 +6278,8 @@
      *         is not supported on the device.
      * @throws SecurityException if {@code admin} is not a device owner.
      */
-    public List<SecurityEvent> retrievePreRebootSecurityLogs(@NonNull ComponentName admin) {
+    public @Nullable List<SecurityEvent> retrievePreRebootSecurityLogs(
+            @NonNull ComponentName admin) {
         throwIfParentInstance("retrievePreRebootSecurityLogs");
         try {
             ParceledListSlice<SecurityEvent> list = mService.retrievePreRebootSecurityLogs(admin);
@@ -6391,7 +6398,7 @@
      * @return The organization name or {@code null} if none is set.
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
-    public CharSequence getOrganizationName(@NonNull ComponentName admin) {
+    public @Nullable CharSequence getOrganizationName(@NonNull ComponentName admin) {
         throwIfParentInstance("getOrganizationName");
         try {
             return mService.getOrganizationName(admin);
@@ -6408,7 +6415,7 @@
      *
      * @hide
      */
-    public CharSequence getOrganizationNameForUser(int userHandle) {
+    public @Nullable CharSequence getOrganizationNameForUser(int userHandle) {
         try {
             return mService.getOrganizationNameForUser(userHandle);
         } catch (RemoteException re) {
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 4cbff5f..e2ebd46 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -26,6 +26,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
+
 /**
  * Information you can retrieve about a particular application
  * activity or receiver. This corresponds to information collected
@@ -763,6 +765,13 @@
      */
     public String parentActivityName;
 
+    /**
+     * Screen rotation animation desired by the activity, with values as defined
+     * for {@link android.view.WindowManager.LayoutParams#rotationAnimation}.
+     * @hide
+     */
+    public int rotationAnimation = ROTATION_ANIMATION_ROTATE;
+
     /** @hide */
     public static final int LOCK_TASK_LAUNCH_MODE_DEFAULT = 0;
     /** @hide */
@@ -822,6 +831,7 @@
         windowLayout = orig.windowLayout;
         resizeMode = orig.resizeMode;
         requestedVrComponent = orig.requestedVrComponent;
+        rotationAnimation = orig.rotationAnimation;
     }
 
     /**
@@ -972,6 +982,7 @@
         }
         dest.writeInt(resizeMode);
         dest.writeString(requestedVrComponent);
+        dest.writeInt(rotationAnimation);
     }
 
     public static final Parcelable.Creator<ActivityInfo> CREATOR
@@ -1006,6 +1017,7 @@
         }
         resizeMode = source.readInt();
         requestedVrComponent = source.readString();
+        rotationAnimation = source.readInt();
     }
 
     /**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 12e8d487..13c8ab1 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -98,6 +98,7 @@
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
 
 /**
  * Parser for package files (APKs) on disk. This supports apps packaged either
@@ -2599,9 +2600,10 @@
         perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);
 
         if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_FLAGS) != 0) {
-            if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
+            if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_EPHEMERAL) == 0
+                    && (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
                     PermissionInfo.PROTECTION_SIGNATURE) {
-                outError[0] = "<permission>  protectionLevel specifies a flag but is "
+                outError[0] = "<permission>  protectionLevel specifies a non-ephemeral flag but is "
                         + "not based on signature type";
                 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                 return null;
@@ -3572,6 +3574,9 @@
 
             a.info.requestedVrComponent =
                 sa.getString(R.styleable.AndroidManifestActivity_enableVrMode);
+
+            a.info.rotationAnimation =
+                sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, ROTATION_ANIMATION_ROTATE);
         } else {
             a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
             a.info.configChanges = 0;
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 65e0b92..6901ba1 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -17,6 +17,7 @@
 package android.content.pm;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -119,6 +120,17 @@
      */
     public static final int PROTECTION_FLAG_SETUP = 0x800;
 
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>ephemeral</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_EPHEMERAL = 0x1000;
+
     /**
      * Mask for {@link #protectionLevel}: the basic protection type.
      */
@@ -127,7 +139,7 @@
     /**
      * Mask for {@link #protectionLevel}: additional flag bits.
      */
-    public static final int PROTECTION_MASK_FLAGS = 0xff0;
+    public static final int PROTECTION_MASK_FLAGS = 0xfff0;
 
     /**
      * The level of access this permission is protecting, as per
@@ -236,6 +248,9 @@
         if ((level&PermissionInfo.PROTECTION_FLAG_SETUP) != 0) {
             protLevel += "|setup";
         }
+        if ((level&PermissionInfo.PROTECTION_FLAG_EPHEMERAL) != 0) {
+            protLevel += "|ephemeral";
+        }
         return protLevel;
     }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d3a978c..68b7b3a 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6001,6 +6001,12 @@
         public static final String DOZE_ENABLED = "doze_enabled";
 
         /**
+         * Whether doze should be always on.
+         * @hide
+         */
+        public static final String DOZE_ALWAYS_ON = "doze_always_on";
+
+        /**
          * Whether the device should pulse on pick up gesture.
          * @hide
          */
@@ -8287,6 +8293,9 @@
         /** {@hide} */
         public static final String
                 BLUETOOTH_SAP_PRIORITY_PREFIX = "bluetooth_sap_priority_";
+        /** {@hide} */
+        public static final String
+                BLUETOOTH_PAN_PRIORITY_PREFIX = "bluetooth_pan_priority_";
 
         /**
          * Device Idle (Doze) specific settings.
@@ -8464,6 +8473,14 @@
         }
 
         /**
+         * Get the key that retrieves a bluetooth pan client priority.
+         * @hide
+         */
+        public static final String getBluetoothPanPriorityKey(String address) {
+            return BLUETOOTH_PAN_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
+        }
+
+        /**
          * Get the key that retrieves a bluetooth map priority.
          * @hide
          */
diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl
new file mode 100644
index 0000000..830591d
--- /dev/null
+++ b/core/java/android/view/IPinnedStackController.aidl
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2016, 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.view;
+
+import android.graphics.Rect;
+
+/**
+ * An interface to the PinnedStackController to update it of state changes, and to query
+ * information based on the current state.
+ *
+ * @hide
+ */
+interface IPinnedStackController {
+
+    /**
+     * Notifies the controller that the user is currently interacting with the PIP.
+     */
+    oneway void setInInteractiveMode(boolean inInteractiveMode);
+}
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
new file mode 100644
index 0000000..3050dbb
--- /dev/null
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2016, 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.view;
+
+import android.view.IPinnedStackController;
+
+/**
+  * Listener for changes to the pinned stack made by the WindowManager.
+  *
+  * @hide
+  */
+oneway interface IPinnedStackListener {
+
+    /**
+     * Called when the listener is registered and provides an interface to call back to the pinned
+     * stack controller to update the controller of the pinned stack state.
+     */
+    void onListenerRegistered(IPinnedStackController controller);
+
+    /**
+     * Called when window manager decides to adjust the pinned stack bounds, or when the listener
+     * is first registered to allow the listener to synchronized its state with the controller.
+     */
+    void onBoundsChanged(boolean adjustedForIme);
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index e42b42a..986ff46 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -34,6 +34,7 @@
 import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.IDockedStackListener;
 import android.view.IOnKeyguardExitResult;
+import android.view.IPinnedStackListener;
 import android.view.IRotationWatcher;
 import android.view.IWindowSession;
 import android.view.IWindowSessionCallback;
@@ -426,6 +427,21 @@
     void registerDockedStackListener(IDockedStackListener listener);
 
     /**
+     * Registers a listener that will be called when the pinned stack state changes.
+     */
+    void registerPinnedStackListener(int displayId, IPinnedStackListener listener);
+
+    /**
+     * Returns the initial bounds that PIP will be shown when it is first started.
+     */
+    Rect getPictureInPictureDefaultBounds(int displayId);
+
+    /**
+     * Returns the bounds that the PIP can move on the screen in the current PIP state.
+     */
+    Rect getPictureInPictureMovementBounds(int displayId);
+
+    /**
      * Updates the dim layer used while resizing.
      *
      * @param visible Whether the dim layer should be visible.
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 8023201..71c1d62 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -860,32 +860,35 @@
             android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;  // 0x00000001
 
     /**
-     * Called by the input method to commit a content such as PNG image to the editor.
+     * Called by the input method to commit content such as a PNG image to the editor.
      *
-     * <p>In order to avoid variety of compatibility issues, this focuses on a simple use case,
-     * where we expect editors and IMEs work cooperatively as follows:</p>
+     * <p>In order to avoid a variety of compatibility issues, this focuses on a simple use case,
+     * where editors and IMEs are expected to work cooperatively as follows:</p>
      * <ul>
-     *     <li>Editor must keep {@link EditorInfo#contentMimeTypes} to be {@code null} if it does
+     *     <li>Editor must keep {@link EditorInfo#contentMimeTypes} equal to {@code null} if it does
      *     not support this method at all.</li>
      *     <li>Editor can ignore this request when the MIME type specified in
-     *     {@code inputContentInfo} does not match to any of {@link EditorInfo#contentMimeTypes}.
+     *     {@code inputContentInfo} does not match any of {@link EditorInfo#contentMimeTypes}.
      *     </li>
-     *     <li>Editor can ignore the cursor position when inserting the provided context.</li>
+     *     <li>Editor can ignore the cursor position when inserting the provided content.</li>
      *     <li>Editor can return {@code true} asynchronously, even before it starts loading the
      *     content.</li>
-     *     <li>Editor should provide a way to delete the content inserted by this method, or revert
-     *     the effect caused by this method.</li>
+     *     <li>Editor should provide a way to delete the content inserted by this method or to
+     *     revert the effect caused by this method.</li>
      *     <li>IME should not call this method when there is any composing text, in case calling
-     *     this method causes focus change.</li>
+     *     this method causes a focus change.</li>
      *     <li>IME should grant a permission for the editor to read the content. See
      *     {@link EditorInfo#packageName} about how to obtain the package name of the editor.</li>
      * </ul>
      *
      * @param inputContentInfo Content to be inserted.
-     * @param flags {@code 0} or {@link #INPUT_CONTENT_GRANT_READ_URI_PERMISSION}.
+     * @param flags {@link #INPUT_CONTENT_GRANT_READ_URI_PERMISSION} if the content provider
+     * allows {@link android.R.styleable#AndroidManifestProvider_grantUriPermissions
+     * grantUriPermissions} or {@code 0} if the application does not need to call
+     * {@link InputContentInfo#requestPermission()}.
      * @param opts optional bundle data. This can be {@code null}.
-     * @return {@code true} if this request is accepted by the application, no matter if the request
-     * is already handled or still being handled in background.
+     * @return {@code true} if this request is accepted by the application, whether the request
+     * is already handled or still being handled in background, {@code false} otherwise.
      */
     public boolean commitContent(@NonNull InputContentInfo inputContentInfo, int flags,
             @Nullable Bundle opts);
diff --git a/core/java/com/android/internal/policy/PipMotionHelper.java b/core/java/com/android/internal/policy/PipMotionHelper.java
new file mode 100644
index 0000000..0543442
--- /dev/null
+++ b/core/java/com/android/internal/policy/PipMotionHelper.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+/**
+ * A helper to animate the PIP.
+ */
+public class PipMotionHelper {
+
+    private static final String TAG = "PipMotionHelper";
+
+    private static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
+    private static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+    private static final int DEFAULT_DURATION = 225;
+
+    private IActivityManager mActivityManager;
+    private Handler mHandler;
+
+    public PipMotionHelper(Handler handler) {
+        mHandler = handler;
+    }
+
+    /**
+     * Moves the PIP to give given {@param bounds}.
+     */
+    public void resizeToBounds(Rect toBounds) {
+        mHandler.post(() -> {
+            if (mActivityManager == null) {
+                mActivityManager = ActivityManagerNative.getDefault();
+            }
+            try {
+                mActivityManager.resizePinnedStack(toBounds, null /* tempPinnedTaskBounds */);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Could not move pinned stack to bounds: " + toBounds, e);
+            }
+        });
+    }
+
+    /**
+     * Creates an animation to move the PIP to give given {@param toBounds} with the default
+     * animation properties.
+     */
+    public ValueAnimator createAnimationToBounds(Rect fromBounds, Rect toBounds) {
+        return createAnimationToBounds(fromBounds, toBounds, DEFAULT_DURATION, FAST_OUT_SLOW_IN,
+                null);
+    }
+
+    /**
+     * Creates an animation to move the PIP to give given {@param toBounds}.
+     */
+    public ValueAnimator createAnimationToBounds(Rect fromBounds, Rect toBounds, int duration,
+            Interpolator interpolator, ValueAnimator.AnimatorUpdateListener updateListener) {
+        ValueAnimator anim = ValueAnimator.ofObject(RECT_EVALUATOR, fromBounds, toBounds);
+        anim.setDuration(duration);
+        anim.setInterpolator(interpolator);
+        anim.addUpdateListener((ValueAnimator animation) -> {
+            resizeToBounds((Rect) animation.getAnimatedValue());
+        });
+        if (updateListener != null) {
+            anim.addUpdateListener(updateListener);
+        }
+        return anim;
+    }
+
+
+}
diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
index 793b228..51804b0 100644
--- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
@@ -48,21 +48,12 @@
     private final ArrayList<Integer> mSnapGravities = new ArrayList<>();
     private final int mSnapMode = SNAP_MODE_CORNERS_ONLY;
 
-    private final Scroller mScroller;
-    private final Rect mDisplayBounds = new Rect();
+    private Scroller mScroller;
     private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
 
-    public PipSnapAlgorithm(Context context, int displayId) {
-        final DisplayManager displayManager =
-                (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
-        final ViewConfiguration viewConfig = ViewConfiguration.get(context);
-        final Point displaySize = new Point();
-        displayManager.getDisplay(displayId).getRealSize(displaySize);
+    public PipSnapAlgorithm(Context context) {
         mContext = context;
-        mDisplayBounds.set(0, 0, displaySize.x, displaySize.y);
         mOrientation = context.getResources().getConfiguration().orientation;
-        mScroller = new Scroller(context);
-        mScroller.setFriction(viewConfig.getScrollFriction() * SCROLL_FRICTION_MULTIPLIER);
         calculateSnapTargets();
     }
 
@@ -74,6 +65,11 @@
     public Rect findClosestSnapBounds(Rect movementBounds, Rect stackBounds, float velocityX,
             float velocityY) {
         final Rect finalStackBounds = new Rect(stackBounds);
+        if (mScroller == null) {
+            final ViewConfiguration viewConfig = ViewConfiguration.get(mContext);
+            mScroller = new Scroller(mContext);
+            mScroller.setFriction(viewConfig.getScrollFriction() * SCROLL_FRICTION_MULTIPLIER);
+        }
         mScroller.fling(stackBounds.left, stackBounds.top,
                 (int) velocityX, (int) velocityY,
                 movementBounds.left, movementBounds.right,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index de6f2c5..b38bb1e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -723,7 +723,7 @@
         android:permissionGroup="android.permission-group.LOCATION"
         android:label="@string/permlab_accessFineLocation"
         android:description="@string/permdesc_accessFineLocation"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous|ephemeral" />
 
     <!-- Allows an app to access approximate location.
          <p>Protection level: dangerous
@@ -732,7 +732,7 @@
         android:permissionGroup="android.permission-group.LOCATION"
         android:label="@string/permlab_accessCoarseLocation"
         android:description="@string/permdesc_accessCoarseLocation"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous|ephemeral" />
 
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the device telephony                         -->
@@ -763,7 +763,7 @@
         android:permissionGroup="android.permission-group.PHONE"
         android:label="@string/permlab_readPhoneState"
         android:description="@string/permdesc_readPhoneState"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous|ephemeral" />
 
     <!-- Allows an application to initiate a phone call without going through
         the Dialer user interface for the user to confirm the call.
@@ -1164,7 +1164,7 @@
     <permission android:name="android.permission.INTERNET"
         android:description="@string/permdesc_createNetworkSockets"
         android:label="@string/permlab_createNetworkSockets"
-        android:protectionLevel="normal" />
+        android:protectionLevel="normal|ephemeral" />
 
     <!-- Allows applications to access information about networks.
          <p>Protection level: normal
@@ -1354,7 +1354,7 @@
     <permission android:name="android.permission.VIBRATE"
         android:label="@string/permlab_vibrate"
         android:description="@string/permdesc_vibrate"
-        android:protectionLevel="normal" />
+        android:protectionLevel="normal|ephemeral" />
 
     <!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen
          from dimming.
@@ -1363,7 +1363,7 @@
     <permission android:name="android.permission.WAKE_LOCK"
         android:label="@string/permlab_wakeLock"
         android:description="@string/permdesc_wakeLock"
-        android:protectionLevel="normal" />
+        android:protectionLevel="normal|ephemeral" />
 
     <!-- Allows using the device's IR transmitter, if available.
          <p>Protection level: normal
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index d1fbebb..07f6aa8 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -67,8 +67,8 @@
     </plurals>
     <string name="imei" msgid="2625429890869005782">"IMEI"</string>
     <string name="meid" msgid="4841221237681254195">"MEID"</string>
-    <string name="ClipMmi" msgid="6952821216480289285">"ID de l\'emissor (trucada entrant)"</string>
-    <string name="ClirMmi" msgid="7784673673446833091">"ID de l\'emissor (trucada de sortida)"</string>
+    <string name="ClipMmi" msgid="6952821216480289285">"Identificador de trucada (trucada entrant)"</string>
+    <string name="ClirMmi" msgid="7784673673446833091">"Identificador de trucada (trucada de sortida)"</string>
     <string name="ColpMmi" msgid="3065121483740183974">"Identificador de la línia connectada"</string>
     <string name="ColrMmi" msgid="4996540314421889589">"Restricció de l\'identificador de la línia connectada"</string>
     <string name="CfMmi" msgid="5123218989141573515">"Desviació de trucades"</string>
@@ -82,12 +82,12 @@
     <string name="RuacMmi" msgid="7827887459138308886">"Rebuig de trucades molestes no desitjades"</string>
     <string name="CndMmi" msgid="3116446237081575808">"Lliurament de número que truca"</string>
     <string name="DndMmi" msgid="1265478932418334331">"No molesteu"</string>
-    <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"El valor predeterminat de l\'identificador de l\'emissor és restringit. Següent trucada: restringit"</string>
-    <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"El valor predeterminat de l\'identificador de l\'emissor és restringit. Següent trucada: no restringit"</string>
-    <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"El valor predeterminat de l\'identificador de l\'emissor és no restringit. Següent trucada: restringit"</string>
-    <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"El valor predeterminat de l\'identificador de l\'emissor és no restringit. Següent trucada: no restringit"</string>
+    <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"El valor predeterminat de l\'identificador de trucada és restringit. Trucada següent: restringit"</string>
+    <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"El valor predeterminat de l\'identificador de trucada és restringit. Trucada següent: no restringit"</string>
+    <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"El valor predeterminat de l\'identificador de trucada és no restringit. Trucada següent: restringit"</string>
+    <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"El valor predeterminat de l\'identificador de trucada és no restringit. Trucada següent: no restringit"</string>
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"No s\'ha proveït el servei."</string>
-    <string name="CLIRPermanent" msgid="3377371145926835671">"No pots canviar la configuració de l\'identificador de l\'emissor."</string>
+    <string name="CLIRPermanent" msgid="3377371145926835671">"No pots canviar la configuració de l\'identificador de trucada."</string>
     <string name="RestrictedOnData" msgid="8653794784690065540">"El servei de dades està bloquejat."</string>
     <string name="RestrictedOnEmergency" msgid="6581163779072833665">"El servei d\'emergència està bloquejat."</string>
     <string name="RestrictedOnNormal" msgid="4953867011389750673">"El servei de veu està bloquejat."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index ac74164..340010f 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -671,7 +671,7 @@
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nödsamtalsnummer"</string>
     <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ingen tjänst"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skärmen har låsts."</string>
-    <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Tryck på Menu om du vill låsa upp eller ringa nödsamtal."</string>
+    <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Tryck på Menu för att låsa upp eller ringa nödsamtal."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tryck på Menu för att låsa upp."</string>
     <string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Rita grafiskt lösenord för att låsa upp"</string>
     <string name="lockscreen_emergency_call" msgid="5298642613417801888">"Nödsamtal"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index cad5854..967c4ad 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -242,6 +242,9 @@
         <!-- Additional flag from base permission type: this permission can be automatically
             granted to the setup wizard app -->
         <flag name="setup" value="0x800" />
+        <!-- Additional flag from base permission type: this permission can be granted to ephemeral
+             apps -->
+        <flag name="ephemeral" value="0x1000" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
@@ -575,6 +578,20 @@
          transtion from one VR activity to another. -->
     <attr name="enableVrMode" format="string" />
 
+    <!-- Flag allowing the activity to specify which screen rotation animation
+         it desires.  Valid values are "rotate", "crossfade", and "jumpcut"
+         as described in {@link android.view.WindowManager.LayoutParams#rotationAnimation}.
+         Specifying your Rotation animation in the WindowManager.LayoutParams
+         may be racy with app startup and updattransitions occuring during application startup and thusly
+         the manifest attribute is preferred.
+    -->
+    <attr name="rotationAnimation">
+      <flag name="rotate" value= "0" />
+      <flag name="crossfade" value = "1" />
+      <flag name="jumpcut" value = "2" />
+      <flag name="seamless" value = "3" />
+    </attr>
+
     <!-- Specify the order in which content providers hosted by a process
          are instantiated when that process is created.  Not needed unless
          you have providers with dependencies between each other, to make
@@ -1923,6 +1940,7 @@
         <!-- @hide This activity is a launcher which should always show up on the top of others.
              This attribute is ignored if the activity isn't a launcher. -->
         <attr name="onTopLauncher" format="boolean" />
+        <attr name="rotationAnimation"/>
     </declare-styleable>
 
     <!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5df33bd..567e920 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1368,6 +1368,9 @@
     <!-- Whether supported profiles should be reloaded upon enabling bluetooth -->
     <bool name="config_bluetooth_reload_supported_profiles_when_enabled">false</bool>
 
+    <!-- Enabling autoconnect over pan -->
+    <bool name="config_bluetooth_pan_enable_autoconnect">false</bool>
+
     <!-- The default data-use polling period. -->
     <integer name="config_datause_polling_period_sec">600</integer>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 7999e7e..c06a93d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2765,4 +2765,5 @@
     </public-group>
 
     <public type="attr" name="min" />
+    <public type="attr" name="rotationAnimation" />
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 48cea84..e946136 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -368,6 +368,7 @@
   <java-symbol type="integer" name="config_bluetooth_rx_cur_ma" />
   <java-symbol type="integer" name="config_bluetooth_tx_cur_ma" />
   <java-symbol type="integer" name="config_bluetooth_operating_voltage_mv" />
+  <java-symbol type="bool" name="config_bluetooth_pan_enable_autoconnect" />
   <java-symbol type="bool" name="config_bluetooth_reload_supported_profiles_when_enabled" />
   <java-symbol type="integer" name="config_cursorWindowSize" />
   <java-symbol type="integer" name="config_drawLockTimeoutMillis" />
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 4fe866f..06eb829 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -18,6 +18,12 @@
     hwui/MinikinUtils.cpp \
     hwui/PaintImpl.cpp \
     hwui/Typeface.cpp \
+    pipeline/skia/GLFunctorDrawable.cpp \
+    pipeline/skia/LayerDrawable.cpp \
+    pipeline/skia/RenderNodeDrawable.cpp \
+    pipeline/skia/ReorderBarrierDrawables.cpp \
+    pipeline/skia/SkiaDisplayList.cpp \
+    pipeline/skia/SkiaRecordingCanvas.cpp \
     renderstate/Blend.cpp \
     renderstate/MeshState.cpp \
     renderstate/OffscreenBufferPool.cpp \
@@ -94,7 +100,6 @@
     ShadowTessellator.cpp \
     SkiaCanvas.cpp \
     SkiaCanvasProxy.cpp \
-    SkiaDisplayList.cpp \
     SkiaShader.cpp \
     Snapshot.cpp \
     SpotShadow.cpp \
@@ -169,6 +174,7 @@
 hwui_c_includes += \
     external/skia/include/private \
     external/skia/src/core \
+    external/skia/src/effects \
     external/harfbuzz_ng/src \
     external/freetype/include
 
@@ -284,6 +290,7 @@
     tests/unit/MeshStateTests.cpp \
     tests/unit/OffscreenBufferPoolTests.cpp \
     tests/unit/OpDumperTests.cpp \
+    tests/unit/RenderNodeDrawableTests.cpp \
     tests/unit/RecordingCanvasTests.cpp \
     tests/unit/RenderNodeTests.cpp \
     tests/unit/RenderPropertiesTests.cpp \
diff --git a/libs/hwui/NinePatchUtils.h b/libs/hwui/NinePatchUtils.h
index 7a271b7..e989a46 100644
--- a/libs/hwui/NinePatchUtils.h
+++ b/libs/hwui/NinePatchUtils.h
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#pragma once
+
 namespace android {
 namespace NinePatchUtils {
 
@@ -34,5 +36,61 @@
     }
 }
 
+static inline int NumDistinctRects(const SkCanvas::Lattice& lattice) {
+    int xRects;
+    if (lattice.fXCount > 0) {
+        xRects = (0 == lattice.fXDivs[0]) ? lattice.fXCount : lattice.fXCount + 1;
+    } else {
+        xRects = 1;
+    }
+
+    int yRects;
+    if (lattice.fYCount > 0) {
+        yRects = (0 == lattice.fYDivs[0]) ? lattice.fYCount : lattice.fYCount + 1;
+    } else {
+        yRects = 1;
+    }
+    return xRects * yRects;
+}
+
+static inline void SetLatticeFlags(SkCanvas::Lattice* lattice, SkCanvas::Lattice::Flags* flags,
+        int numFlags, const Res_png_9patch& chunk) {
+    lattice->fFlags = flags;
+    sk_bzero(flags, numFlags * sizeof(SkCanvas::Lattice::Flags));
+
+    bool needPadRow = lattice->fYCount > 0 && 0 == lattice->fYDivs[0];
+    bool needPadCol = lattice->fXCount > 0 && 0 == lattice->fXDivs[0];
+
+    int yCount = lattice->fYCount;
+    if (needPadRow) {
+        // Skip flags for the degenerate first row of rects.
+        flags += lattice->fXCount + 1;
+        yCount--;
+    }
+
+    int i = 0;
+    bool setFlags = false;
+    for (int y = 0; y < yCount + 1; y++) {
+        for (int x = 0; x < lattice->fXCount + 1; x++) {
+            if (0 == x && needPadCol) {
+                // First rect of each column is degenerate, skip the flag.
+                flags++;
+                continue;
+            }
+
+            if (0 == chunk.getColors()[i++]) {
+                *flags = SkCanvas::Lattice::kTransparent_Flags;
+                setFlags = true;
+            }
+
+            flags++;
+        }
+    }
+
+    if (!setFlags) {
+        lattice->fFlags = nullptr;
+    }
+}
+
 }; // namespace NinePatchUtils
 }; // namespace android
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index a05c744..3819c5e 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -32,7 +32,7 @@
 #include "DisplayList.h"
 #include "Matrix.h"
 #include "RenderProperties.h"
-#include "SkiaDisplayList.h"
+#include "pipeline/skia/SkiaDisplayList.h"
 
 #include <vector>
 
@@ -50,7 +50,6 @@
 class FrameBuilder;
 class OffscreenBuffer;
 class Rect;
-class SkiaDisplayList;
 class SkiaShader;
 struct RenderNodeOp;
 
@@ -61,6 +60,10 @@
 class RenderNode;
 }
 
+namespace skiapipeline {
+    class SkiaDisplayList;
+}
+
 /**
  * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
  *
@@ -294,14 +297,14 @@
      * Detach and transfer ownership of an already allocated displayList for use
      * in recording updated content for this renderNode
      */
-    std::unique_ptr<SkiaDisplayList> detachAvailableList() {
+    std::unique_ptr<skiapipeline::SkiaDisplayList> detachAvailableList() {
         return std::move(mAvailableDisplayList);
     }
 
     /**
      * Attach unused displayList to this node for potential future reuse.
      */
-    void attachAvailableList(SkiaDisplayList* skiaDisplayList) {
+    void attachAvailableList(skiapipeline::SkiaDisplayList* skiaDisplayList) {
         mAvailableDisplayList.reset(skiaDisplayList);
     }
 
@@ -337,7 +340,7 @@
      *  2) It is replaced with the displayList from the next completed frame
      *  3) It is detached and used to to record a new displayList for a later frame
      */
-    std::unique_ptr<SkiaDisplayList> mAvailableDisplayList;
+    std::unique_ptr<skiapipeline::SkiaDisplayList> mAvailableDisplayList;
 
     /**
      * An offscreen rendering target used to contain the contents this RenderNode
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index c48b4dc..b6ac48f 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -21,6 +21,7 @@
 #include "VectorDrawable.h"
 #include "hwui/Bitmap.h"
 #include "hwui/MinikinUtils.h"
+#include "pipeline/skia/AnimatedDrawables.h"
 
 #include <SkDrawable.h>
 #include <SkDevice.h>
@@ -57,6 +58,8 @@
     mCanvas.reset(new SkCanvas(bitmap));
 }
 
+SkiaCanvas::~SkiaCanvas() {}
+
 void SkiaCanvas::reset(SkCanvas* skiaCanvas) {
     mCanvas.reset(SkRef(skiaCanvas));
     mSaveStack.reset(nullptr);
@@ -671,62 +674,6 @@
                          indexCount, tmpPaint);
 }
 
-static inline int num_distinct_rects(const SkCanvas::Lattice& lattice) {
-    int xRects;
-    if (lattice.fXCount > 0) {
-        xRects = (0 == lattice.fXDivs[0]) ? lattice.fXCount : lattice.fXCount + 1;
-    } else {
-        xRects = 1;
-    }
-
-    int yRects;
-    if (lattice.fYCount > 0) {
-        yRects = (0 == lattice.fYDivs[0]) ? lattice.fYCount : lattice.fYCount + 1;
-    } else {
-        yRects = 1;
-    }
-    return xRects * yRects;
-}
-
-static inline void set_lattice_flags(SkCanvas::Lattice* lattice, SkCanvas::Lattice::Flags* flags,
-                                     int numFlags, const Res_png_9patch& chunk) {
-    lattice->fFlags = flags;
-    sk_bzero(flags, numFlags * sizeof(SkCanvas::Lattice::Flags));
-
-    bool needPadRow = lattice->fYCount > 0 && 0 == lattice->fYDivs[0];
-    bool needPadCol = lattice->fXCount > 0 && 0 == lattice->fXDivs[0];
-
-    int yCount = lattice->fYCount;
-    if (needPadRow) {
-        // Skip flags for the degenerate first row of rects.
-        flags += lattice->fXCount + 1;
-        yCount--;
-    }
-
-    int i = 0;
-    bool setFlags = false;
-    for (int y = 0; y < yCount + 1; y++) {
-        for (int x = 0; x < lattice->fXCount + 1; x++) {
-            if (0 == x && needPadCol) {
-                // First rect of each column is degenerate, skip the flag.
-                flags++;
-                continue;
-            }
-
-            if (0 == chunk.getColors()[i++]) {
-                *flags = SkCanvas::Lattice::kTransparent_Flags;
-                setFlags = true;
-            }
-
-            flags++;
-        }
-    }
-
-    if (!setFlags) {
-        lattice->fFlags = nullptr;
-    }
-}
-
 void SkiaCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk,
         float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
 
@@ -738,7 +685,7 @@
 
     lattice.fFlags = nullptr;
     int numFlags = 0;
-    if (chunk.numColors > 0 && chunk.numColors == num_distinct_rects(lattice)) {
+    if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) {
         // We can expect the framework to give us a color for every distinct rect.
         // Skia requires a flag for every rect.
         numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
@@ -746,7 +693,7 @@
 
     SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags);
     if (numFlags > 0) {
-        set_lattice_flags(&lattice, flags.get(), numFlags, chunk);
+        NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk);
     }
 
     lattice.fBounds = nullptr;
@@ -820,69 +767,18 @@
 // Canvas draw operations: Animations
 // ----------------------------------------------------------------------------
 
-class AnimatedRoundRect : public SkDrawable {
- public:
-    AnimatedRoundRect(uirenderer::CanvasPropertyPrimitive* left,
-            uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
-            uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
-            uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* p) :
-            mLeft(left), mTop(top), mRight(right), mBottom(bottom), mRx(rx), mRy(ry), mPaint(p) {}
-
- protected:
-     virtual SkRect onGetBounds() override {
-         return SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value);
-     }
-     virtual void onDraw(SkCanvas* canvas) override {
-         SkRect rect = SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value);
-         canvas->drawRoundRect(rect, mRx->value, mRy->value, mPaint->value);
-     }
-
- private:
-    sp<uirenderer::CanvasPropertyPrimitive> mLeft;
-    sp<uirenderer::CanvasPropertyPrimitive> mTop;
-    sp<uirenderer::CanvasPropertyPrimitive> mRight;
-    sp<uirenderer::CanvasPropertyPrimitive> mBottom;
-    sp<uirenderer::CanvasPropertyPrimitive> mRx;
-    sp<uirenderer::CanvasPropertyPrimitive> mRy;
-    sp<uirenderer::CanvasPropertyPaint> mPaint;
-};
-
-class AnimatedCircle : public SkDrawable {
- public:
-    AnimatedCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y,
-            uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint) :
-            mX(x), mY(y), mRadius(radius), mPaint(paint) {}
-
- protected:
-     virtual SkRect onGetBounds() override {
-         const float x = mX->value;
-         const float y = mY->value;
-         const float radius = mRadius->value;
-         return SkRect::MakeLTRB(x - radius, y - radius, x + radius, y + radius);
-     }
-     virtual void onDraw(SkCanvas* canvas) override {
-         canvas->drawCircle(mX->value, mY->value, mRadius->value, mPaint->value);
-     }
-
- private:
-    sp<uirenderer::CanvasPropertyPrimitive> mX;
-    sp<uirenderer::CanvasPropertyPrimitive> mY;
-    sp<uirenderer::CanvasPropertyPrimitive> mRadius;
-    sp<uirenderer::CanvasPropertyPaint> mPaint;
-};
-
 void SkiaCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
         uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
         uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
         uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) {
-    sk_sp<AnimatedRoundRect> drawable(
-            new AnimatedRoundRect(left, top, right, bottom, rx, ry, paint));
+    sk_sp<uirenderer::skiapipeline::AnimatedRoundRect> drawable(
+            new uirenderer::skiapipeline::AnimatedRoundRect(left, top, right, bottom, rx, ry, paint));
     mCanvas->drawDrawable(drawable.get());
 }
 
 void SkiaCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y,
         uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint) {
-    sk_sp<AnimatedCircle> drawable(new AnimatedCircle(x, y, radius, paint));
+    sk_sp<uirenderer::skiapipeline::AnimatedCircle> drawable(new uirenderer::skiapipeline::AnimatedCircle(x, y, radius, paint));
     mCanvas->drawDrawable(drawable.get());
 }
 
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index d1edff9..a0cdfcb 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -40,6 +40,8 @@
      */
     explicit SkiaCanvas(SkCanvas* canvas);
 
+    virtual ~SkiaCanvas();
+
     virtual SkCanvas* asSkCanvas() override {
         return mCanvas.get();
     }
diff --git a/libs/hwui/SkiaDrawables.h b/libs/hwui/SkiaDrawables.h
deleted file mode 100644
index a1ceeaa..0000000
--- a/libs/hwui/SkiaDrawables.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#pragma once
-
-#include "Layer.h"
-#include "RenderNode.h"
-
-#include <SkCanvas.h>
-#include <SkDrawable.h>
-#include <SkMatrix.h>
-
-#include <utils/RefBase.h>
-#include <utils/FatVector.h>
-#include <utils/Functor.h>
-
-namespace android {
-
-class Functor;
-
-namespace uirenderer {
-
-
-class RenderProperties;
-class OffscreenBuffer;
-class GlFunctorLifecycleListener;
-class SkiaDisplayList;
-
-/**
- * This drawable wraps a RenderNode and enables it to be recorded into a list
- * of Skia drawing commands.
- */
-class RenderNodeDrawable : public SkDrawable {
-public:
-    explicit RenderNodeDrawable(RenderNode* node, SkCanvas* canvas)
-            : mRenderNode(node)
-            , mRecordedTransform(canvas->getTotalMatrix()) {}
-
-    /**
-     * The renderNode (and its properties) that is to be drawn
-     */
-    RenderNode* getRenderNode() const { return mRenderNode.get(); }
-
-    /**
-     * Returns the transform on the canvas at time of recording and is used for
-     * computing total transform without rerunning DL contents.
-     */
-    const SkMatrix& getRecordedMatrix() const { return mRecordedTransform; }
-
-protected:
-    virtual SkRect onGetBounds() override {
-        // We don't want to enable a record time quick reject because the properties
-        // of the RenderNode may be updated on subsequent frames.
-        return SkRect::MakeLargest();
-    }
-    virtual void onDraw(SkCanvas* canvas) override { /* TODO */ }
-
-private:
-    sp<RenderNode> mRenderNode;
-    const SkMatrix mRecordedTransform;
-};
-
-/**
- * This drawable wraps a OpenGL functor enabling it to be recorded into a list
- * of Skia drawing commands.
- */
-class GLFunctorDrawable : public SkDrawable {
-public:
-    GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
-            : mFunctor(functor)
-            , mListener(listener) {
-        canvas->getClipBounds(&mBounds);
-    }
-    virtual ~GLFunctorDrawable() {}
-
-    void syncFunctor() const { (*mFunctor)(DrawGlInfo::kModeSync, nullptr); }
-
- protected:
-    virtual SkRect onGetBounds() override { return mBounds; }
-    virtual void onDraw(SkCanvas* canvas) override { /* TODO */ }
-
- private:
-     Functor* mFunctor;
-     sp<GlFunctorLifecycleListener> mListener;
-     SkRect mBounds;
-};
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h
new file mode 100644
index 0000000..44c494f
--- /dev/null
+++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include "CanvasProperty.h"
+#include <utils/RefBase.h>
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class AnimatedRoundRect : public SkDrawable {
+public:
+    AnimatedRoundRect(uirenderer::CanvasPropertyPrimitive* left,
+            uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
+            uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
+            uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* p)
+            : mLeft(left)
+            , mTop(top)
+            , mRight(right)
+            , mBottom(bottom)
+            , mRx(rx)
+            , mRy(ry)
+            , mPaint(p) {}
+
+protected:
+    virtual SkRect onGetBounds() override {
+        return SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value);
+    }
+    virtual void onDraw(SkCanvas* canvas) override {
+        SkRect rect = SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value);
+        canvas->drawRoundRect(rect, mRx->value, mRy->value, mPaint->value);
+    }
+
+private:
+    sp<uirenderer::CanvasPropertyPrimitive> mLeft;
+    sp<uirenderer::CanvasPropertyPrimitive> mTop;
+    sp<uirenderer::CanvasPropertyPrimitive> mRight;
+    sp<uirenderer::CanvasPropertyPrimitive> mBottom;
+    sp<uirenderer::CanvasPropertyPrimitive> mRx;
+    sp<uirenderer::CanvasPropertyPrimitive> mRy;
+    sp<uirenderer::CanvasPropertyPaint> mPaint;
+};
+
+class AnimatedCircle : public SkDrawable {
+public:
+    AnimatedCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y,
+            uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint)
+            : mX(x)
+            , mY(y)
+            , mRadius(radius)
+            , mPaint(paint) {}
+
+protected:
+    virtual SkRect onGetBounds() override {
+        const float x = mX->value;
+        const float y = mY->value;
+        const float radius = mRadius->value;
+        return SkRect::MakeLTRB(x - radius, y - radius, x + radius, y + radius);
+    }
+    virtual void onDraw(SkCanvas* canvas) override {
+        canvas->drawCircle(mX->value, mY->value, mRadius->value, mPaint->value);
+    }
+
+private:
+    sp<uirenderer::CanvasPropertyPrimitive> mX;
+    sp<uirenderer::CanvasPropertyPrimitive> mY;
+    sp<uirenderer::CanvasPropertyPrimitive> mRadius;
+    sp<uirenderer::CanvasPropertyPaint> mPaint;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
new file mode 100644
index 0000000..fb2134c
--- /dev/null
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GLFunctorDrawable.h"
+#include "GlFunctorLifecycleListener.h"
+#include "RenderNode.h"
+#include "SkClipStack.h"
+#include <private/hwui/DrawGlInfo.h>
+#include <SkPath.h>
+#include <GrContext.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+GLFunctorDrawable::~GLFunctorDrawable() {
+    if(mListener.get() != nullptr) {
+        mListener->onGlFunctorReleased(mFunctor);
+    }
+}
+
+void GLFunctorDrawable::syncFunctor() const {
+    (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
+}
+
+static void setScissor(int viewportHeight, const SkIRect& clip) {
+    SkASSERT(!clip.isEmpty());
+    // transform to Y-flipped GL space, and prevent negatives
+    GLint y = viewportHeight - clip.fBottom;
+    GLint height = (viewportHeight - clip.fTop) - y;
+    glScissor(clip.fLeft, y, clip.width(), height);
+}
+
+void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
+    if (canvas->getGrContext() == nullptr) {
+        SkDEBUGF(("Attempting to draw GLFunctor into an unsupported surface"));
+        return;
+    }
+
+    canvas->flush();
+
+    SkImageInfo canvasInfo = canvas->imageInfo();
+    SkMatrix44 mat4(canvas->getTotalMatrix());
+
+    SkIRect ibounds;
+    canvas->getClipDeviceBounds(&ibounds);
+
+    DrawGlInfo info;
+    info.clipLeft = ibounds.fLeft;
+    info.clipTop = ibounds.fTop;
+    info.clipRight = ibounds.fRight;
+    info.clipBottom = ibounds.fBottom;
+    //   info.isLayer = hasLayer();
+    info.isLayer = false;
+    info.width = canvasInfo.width();
+    info.height = canvasInfo.height();
+    mat4.asColMajorf(&info.transform[0]);
+
+    //apply a simple clip with a scissor or a complex clip with a stencil
+    SkRegion clipRegion;
+    SkPath path;
+    canvas->getClipStack()->asPath(&path);
+    clipRegion.setPath(path, SkRegion(ibounds));
+    if (CC_UNLIKELY(clipRegion.isComplex())) {
+        //It is only a temporary solution to use a scissor to draw the stencil.
+        //There is a bug 31489986 to implement efficiently non-rectangular clips.
+        glDisable(GL_SCISSOR_TEST);
+        glDisable(GL_STENCIL_TEST);
+        glStencilMask(0xff);
+        glClearStencil(0);
+        glClear(GL_STENCIL_BUFFER_BIT);
+        glEnable(GL_SCISSOR_TEST);
+        SkRegion::Cliperator it(clipRegion, ibounds);
+        while (!it.done()) {
+            setScissor(info.height, it.rect());
+            glClearStencil(0x1);
+            glClear(GL_STENCIL_BUFFER_BIT);
+            it.next();
+        }
+        glDisable(GL_SCISSOR_TEST);
+        glStencilFunc(GL_EQUAL, 0x1, 0xff);
+        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+        glEnable(GL_STENCIL_TEST);
+    } else if (clipRegion.isEmpty()) {
+        glDisable(GL_STENCIL_TEST);
+        glDisable(GL_SCISSOR_TEST);
+    } else {
+        glDisable(GL_STENCIL_TEST);
+        glEnable(GL_SCISSOR_TEST);
+        setScissor(info.height, clipRegion.getBounds());
+    }
+
+    (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+
+    canvas->getGrContext()->resetContext();
+ }
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
new file mode 100644
index 0000000..bf39dad
--- /dev/null
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+#include <utils/RefBase.h>
+#include <utils/Functor.h>
+
+namespace android {
+namespace uirenderer {
+
+class GlFunctorLifecycleListener;
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a OpenGL functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class GLFunctorDrawable : public SkDrawable {
+public:
+    GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
+            : mFunctor(functor)
+            , mListener(listener) {
+        canvas->getClipBounds(&mBounds);
+    }
+    virtual ~GLFunctorDrawable();
+
+    void syncFunctor() const;
+
+ protected:
+    virtual SkRect onGetBounds() override { return mBounds; }
+    virtual void onDraw(SkCanvas* canvas) override;
+
+ private:
+     Functor* mFunctor;
+     sp<GlFunctorLifecycleListener> mListener;
+     SkRect mBounds;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
new file mode 100644
index 0000000..f8a181f
--- /dev/null
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerDrawable.h"
+#include "gl/GrGLTypes.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+void LayerDrawable::onDraw(SkCanvas* canvas) {
+    // transform the matrix based on the layer
+    int saveCount = -1;
+    if (!mLayer->getTransform().isIdentity()) {
+        saveCount = canvas->save();
+        SkMatrix transform;
+        mLayer->getTransform().copyTo(transform);
+        canvas->concat(transform);
+    }
+    GrGLTextureInfo externalTexture;
+    externalTexture.fTarget = mLayer->getRenderTarget();
+    externalTexture.fID = mLayer->getTextureId();
+    GrContext* context = canvas->getGrContext();
+    GrBackendTextureDesc textureDescription;
+    textureDescription.fWidth = mLayer->getWidth();
+    textureDescription.fHeight = mLayer->getHeight();
+    textureDescription.fConfig = kRGBA_8888_GrPixelConfig;
+    textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin;
+    textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture);
+    sk_sp<SkImage> layerImage(SkImage::NewFromTexture(context, textureDescription));
+    if (layerImage) {
+        SkPaint paint;
+        paint.setAlpha(mLayer->getAlpha());
+        paint.setBlendMode(mLayer->getMode());
+        paint.setColorFilter(mLayer->getColorFilter());
+        canvas->drawImage(layerImage, 0, 0, &paint);
+    }
+    // restore the original matrix
+    if (saveCount >= 0) {
+        canvas->restoreToCount(saveCount);
+    }
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
new file mode 100644
index 0000000..91e2744
--- /dev/null
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include "Layer.h"
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/*
+ * Draws a layer backed by an OpenGL texture into a SkCanvas.
+ */
+class LayerDrawable : public SkDrawable {
+ public:
+    explicit LayerDrawable(Layer* layer)
+            : mLayer(layer) {}
+
+ protected:
+     virtual SkRect onGetBounds() override {
+         return SkRect::MakeWH(mLayer->getWidth(), mLayer->getHeight());
+     }
+     virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+    sp<Layer> mLayer;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
new file mode 100644
index 0000000..cefa893
--- /dev/null
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "RenderNodeDrawable.h"
+#include "RenderNode.h"
+#include "SkiaDisplayList.h"
+#include "SkiaFrameRenderer.h"
+#include "utils/TraceUtils.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+static void clipOutline(const Outline& outline, SkCanvas* canvas, const SkRect* pendingClip) {
+    SkASSERT(outline.willClip());
+    Rect possibleRect;
+    float radius;
+    LOG_ALWAYS_FATAL_IF(!outline.getAsRoundRect(&possibleRect, &radius),
+            "clipping outlines should be at most roundedRects");
+    SkRect rect = possibleRect.toSkRect();
+    if (radius != 0.0f) {
+        if (pendingClip && !pendingClip->contains(rect)) {
+            canvas->clipRect(*pendingClip);
+        }
+        canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), SkRegion::kIntersect_Op, true);
+    } else {
+        if (pendingClip) {
+            (void)rect.intersect(*pendingClip);
+        }
+        canvas->clipRect(rect);
+    }
+}
+
+const RenderProperties& RenderNodeDrawable::getNodeProperties() const {
+    return mRenderNode->properties();
+}
+
+void RenderNodeDrawable::onDraw(SkCanvas* canvas) {
+    //negative and positive Z order are drawn out of order
+    if (MathUtils::isZero(mRenderNode->properties().getZ())) {
+        this->forceDraw(canvas);
+    }
+}
+
+void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
+    RenderNode* renderNode = mRenderNode.get();
+    if (SkiaFrameRenderer::skpCaptureEnabled()) {
+        SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
+        canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
+    }
+
+    // We only respect the nothingToDraw check when we are composing a layer. This
+    // ensures that we paint the layer even if it is not currently visible in the
+    // event that the properties change and it becomes visible.
+    if (!renderNode->isRenderable() || (renderNode->nothingToDraw() && mComposeLayer)) {
+        return;
+    }
+
+    SkASSERT(renderNode->getDisplayList()->isSkiaDL());
+    SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
+
+    SkAutoCanvasRestore acr(canvas, true);
+
+    const RenderProperties& properties = this->getNodeProperties();
+    if (displayList->mIsProjectionReceiver) {
+        // this node is a projection receiver. We will gather the projected nodes as we draw our
+        // children, and then draw them on top of this node's content.
+        std::vector<ProjectedChild> newList;
+        for (auto& child : displayList->mChildNodes) {
+            // our direct children are not supposed to project into us (nodes project to, at the
+            // nearest, their grandparents). So we "delay" the list's activation one level by
+            // passing it into mNextProjectedChildrenTarget rather than mProjectedChildrenTarget.
+            child.mProjectedChildrenTarget = mNextProjectedChildrenTarget;
+            child.mNextProjectedChildrenTarget = &newList;
+        }
+        // draw ourselves and our children. As a side effect, this will add projected nodes to
+        // newList.
+        this->drawContent(canvas);
+        bool willClip = properties.getOutline().willClip();
+        if (willClip) {
+            canvas->save();
+            clipOutline(properties.getOutline(), canvas, nullptr);
+        }
+        // draw the collected projected nodes
+        for (auto& projectedChild : newList) {
+            canvas->setMatrix(projectedChild.matrix);
+            projectedChild.node->drawContent(canvas);
+        }
+        if (willClip) {
+            canvas->restore();
+        }
+    } else {
+        if (properties.getProjectBackwards() && mProjectedChildrenTarget) {
+            // We are supposed to project this node, so add it to the list and do not actually draw
+            // yet. It will be drawn by its projection receiver.
+            mProjectedChildrenTarget->push_back({ this, canvas->getTotalMatrix() });
+            return;
+        }
+        for (auto& child : displayList->mChildNodes) {
+            // storing these values in the nodes themselves is a bit ugly; they should "really" be
+            // function parameters, but we have to go through the preexisting draw() method and
+            // therefore cannot add additional parameters to it
+            child.mProjectedChildrenTarget = mNextProjectedChildrenTarget;
+            child.mNextProjectedChildrenTarget = mNextProjectedChildrenTarget;
+        }
+        this->drawContent(canvas);
+    }
+    mProjectedChildrenTarget = nullptr;
+    mNextProjectedChildrenTarget = nullptr;
+}
+
+static bool layerNeedsPaint(const LayerProperties& properties,
+                            float alphaMultiplier, SkPaint* paint) {
+    if (alphaMultiplier < 1.0f
+            || properties.alpha() < 255
+            || properties.xferMode() != SkBlendMode::kSrcOver
+            || properties.colorFilter() != nullptr) {
+        paint->setAlpha(properties.alpha() * alphaMultiplier);
+        paint->setBlendMode(properties.xferMode());
+        paint->setColorFilter(properties.colorFilter());
+        return true;
+    }
+    return false;
+}
+
+void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
+    RenderNode* renderNode = mRenderNode.get();
+    float alphaMultiplier = 1.0f;
+    const RenderProperties& properties = renderNode->properties();
+
+    // If we are drawing the contents of layer, we don't want to apply any of
+    // the RenderNode's properties during this pass. Those will all be applied
+    // when the layer is composited.
+    if (mComposeLayer) {
+        setViewProperties(properties, canvas, &alphaMultiplier);
+    }
+
+    //TODO should we let the bound of the drawable do this for us?
+    const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
+    bool quickRejected = properties.getClipToBounds() && canvas->quickReject(bounds);
+    if (!quickRejected) {
+        SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
+        const LayerProperties& layerProperties = properties.layerProperties();
+        // composing a hardware layer
+        if (renderNode->getLayerSurface() && mComposeLayer) {
+            SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
+            SkPaint* paint = nullptr;
+            SkPaint tmpPaint;
+            if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) {
+                paint = &tmpPaint;
+            }
+            renderNode->getLayerSurface()->draw(canvas, 0, 0, paint);
+        // composing a software layer with alpha
+        } else if (properties.effectiveLayerType() == LayerType::Software) {
+            SkPaint paint;
+            bool needsLayer = layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
+            if (needsLayer) {
+                canvas->saveLayer(bounds, &paint);
+            }
+            canvas->drawDrawable(displayList->mDrawable.get());
+            if (needsLayer) {
+                canvas->restore();
+            }
+        } else {
+            canvas->drawDrawable(displayList->mDrawable.get());
+        }
+    }
+}
+
+void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
+        float* alphaMultiplier) {
+    if (properties.getLeft() != 0 || properties.getTop() != 0) {
+        canvas->translate(properties.getLeft(), properties.getTop());
+    }
+    if (properties.getStaticMatrix()) {
+        canvas->concat(*properties.getStaticMatrix());
+    } else if (properties.getAnimationMatrix()) {
+        canvas->concat(*properties.getAnimationMatrix());
+    }
+    if (properties.hasTransformMatrix()) {
+        if (properties.isTransformTranslateOnly()) {
+            canvas->translate(properties.getTranslationX(), properties.getTranslationY());
+        } else {
+            canvas->concat(*properties.getTransformMatrix());
+        }
+    }
+    const bool isLayer = properties.effectiveLayerType() != LayerType::None;
+    int clipFlags = properties.getClippingFlags();
+    if (properties.getAlpha() < 1) {
+        if (isLayer) {
+            clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
+        }
+        if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
+            *alphaMultiplier = properties.getAlpha();
+        } else {
+            // savelayer needed to create an offscreen buffer
+            Rect layerBounds(0, 0, properties.getWidth(), properties.getHeight());
+            if (clipFlags) {
+                properties.getClippingRectForFlags(clipFlags, &layerBounds);
+                clipFlags = 0; // all clipping done by savelayer
+            }
+            SkRect bounds = SkRect::MakeLTRB(layerBounds.left, layerBounds.top,
+                    layerBounds.right, layerBounds.bottom);
+            canvas->saveLayerAlpha(&bounds, (int) (properties.getAlpha() * 255));
+        }
+
+        if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) {
+            // pretend alpha always causes savelayer to warn about
+            // performance problem affecting old versions
+            ATRACE_FORMAT("alpha caused saveLayer %dx%d", properties.getWidth(),
+                    properties.getHeight());
+        }
+    }
+
+    const SkRect* pendingClip = nullptr;
+    SkRect clipRect;
+
+    if (clipFlags) {
+        Rect tmpRect;
+        properties.getClippingRectForFlags(clipFlags, &tmpRect);
+        clipRect = tmpRect.toSkRect();
+        pendingClip = &clipRect;
+    }
+
+    if (properties.getRevealClip().willClip()) {
+        canvas->clipPath(*properties.getRevealClip().getPath(), SkRegion::kIntersect_Op, true);
+    } else if (properties.getOutline().willClip()) {
+        clipOutline(properties.getOutline(), canvas, pendingClip);
+        pendingClip = nullptr;
+    }
+
+    if (pendingClip) {
+        canvas->clipRect(*pendingClip);
+    }
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
new file mode 100644
index 0000000..0762f37
--- /dev/null
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+#include <SkMatrix.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace uirenderer {
+
+class RenderNode;
+class RenderProperties;
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a RenderNode and enables it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class RenderNodeDrawable : public SkDrawable {
+public:
+    /**
+     * This struct contains a pointer to a node that is to be
+     * projected into the drawing order of its closest ancestor
+     * (excluding its parent) that is marked as a projection
+     * receiver. The matrix is used to ensure that the node is
+     * drawn with same matrix as it would have prior to projection.
+     */
+    struct ProjectedChild {
+        const RenderNodeDrawable* node;
+        const SkMatrix matrix;
+    };
+
+    /**
+     * Creates a new RenderNodeDrawable backed by a render node.
+     *
+     * @param node that has to be drawn
+     * @param canvas is a recording canvas used to extract its matrix
+     * @param composeLayer if the node's layer type is RenderLayer this flag determines whether
+     *      we should draw into the contents of the layer or compose the existing contents of the
+     *      layer into the canvas.
+     */
+    explicit RenderNodeDrawable(RenderNode* node, SkCanvas* canvas, bool composeLayer = true)
+            : mRenderNode(node)
+            , mRecordedTransform(canvas->getTotalMatrix())
+            , mComposeLayer(composeLayer) {}
+
+    /**
+     * Draws into the canvas this render node and its children. If the node is marked as a
+     * projection receiver then all projected children (excluding direct children) will be drawn
+     * last. Any projected node not matching those requirements will not be drawn by this function.
+     */
+    void forceDraw(SkCanvas* canvas);
+
+    /**
+     * Returns readonly render properties for this render node.
+     */
+    const RenderProperties& getNodeProperties() const;
+
+    /**
+     * The renderNode (and its properties) that is to be drawn
+     */
+    RenderNode* getRenderNode() const { return mRenderNode.get(); }
+
+    /**
+     * Returns the transform on the canvas at time of recording and is used for
+     * computing total transform without rerunning DL contents.
+     */
+    const SkMatrix& getRecordedMatrix() const { return mRecordedTransform; }
+
+protected:
+    /*
+     * Return the (conservative) bounds of what the drawable will draw.
+     */
+    virtual SkRect onGetBounds() override {
+        // We don't want to enable a record time quick reject because the properties
+        // of the RenderNode may be updated on subsequent frames.
+        return SkRect::MakeLargest();
+    }
+    /**
+     * This function draws into a canvas as forceDraw, but does nothing if the render node has a
+     * non-zero elevation.
+     */
+    virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+    /*
+     * Render node that is wrapped by this class.
+     */
+    sp<RenderNode> mRenderNode;
+
+    /**
+     * Applies the rendering properties of a view onto a SkCanvas.
+     */
+    static void setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
+            float* alphaMultiplier);
+
+    /**
+     * Stores transform on the canvas at time of recording and is used for
+     * computing total transform without rerunning DL contents.
+     */
+    const SkMatrix mRecordedTransform;
+
+    /**
+     * If mRenderNode's layer type is RenderLayer this flag determines whether we
+     * should draw into the contents of the layer or compose the existing contents
+     * of the layer into the canvas.
+     */
+    const bool mComposeLayer;
+
+    /**
+     * List to which we will add any projected children we encounter while walking our descendents.
+     * This pointer is valid only while the node (including its children) is actively being drawn.
+     */
+    std::vector<ProjectedChild>* mProjectedChildrenTarget = nullptr;
+
+    /**
+     * The value to which we should set our children's mProjectedChildrenTarget. We use two pointers
+     * (mProjectedChildrenTarget and mNextProjectedChildrenTarget) because we need to skip over our
+     * parent when looking for a projection receiver.
+     */
+    std::vector<ProjectedChild>* mNextProjectedChildrenTarget = nullptr;
+
+    /*
+     *  Draw the content into a canvas, depending on the render node layer type and mComposeLayer.
+     */
+    void drawContent(SkCanvas* canvas) const;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
new file mode 100644
index 0000000..8d77938
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -0,0 +1,704 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReorderBarrierDrawables.h"
+#include "RenderNode.h"
+#include "SkiaDisplayList.h"
+#include "SkiaFrameRenderer.h"
+
+#include <SkBlurMask.h>
+#include <SkBlurMaskFilter.h>
+#include <SkGaussianEdgeShader.h>
+#include <SkPathOps.h>
+#include <SkRRectsGaussianEdgeShader.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data)
+        : mEndChildIndex(0)
+        , mBeginChildIndex(data->mChildNodes.size())
+        , mDisplayList(data) {
+}
+
+void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
+    if (mChildren.empty()) {
+        //mChildren is allocated and initialized only the first time onDraw is called and cached for
+        //subsequent calls
+        mChildren.reserve(mEndChildIndex - mBeginChildIndex + 1);
+        for (unsigned int i = mBeginChildIndex; i <= mEndChildIndex; i++) {
+            mChildren.push_back(const_cast<RenderNodeDrawable*>(&mDisplayList->mChildNodes[i]));
+        }
+    }
+    std::stable_sort(mChildren.begin(), mChildren.end(),
+        [](RenderNodeDrawable* a, RenderNodeDrawable* b) {
+            const float aZValue = a->getNodeProperties().getZ();
+            const float bZValue = b->getNodeProperties().getZ();
+            return aZValue < bZValue;
+        });
+
+    SkASSERT(!mChildren.empty());
+
+    size_t drawIndex = 0;
+    const size_t endIndex = mChildren.size();
+    while (drawIndex < endIndex) {
+        RenderNodeDrawable* childNode = mChildren[drawIndex];
+        SkASSERT(childNode);
+        const float casterZ = childNode->getNodeProperties().getZ();
+        if (casterZ >= -NON_ZERO_EPSILON) { //draw only children with negative Z
+            return;
+        }
+        childNode->forceDraw(canvas);
+        drawIndex++;
+    }
+}
+
+EndReorderBarrierDrawable::EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier)
+        : mStartBarrier(startBarrier) {
+    mStartBarrier->mEndChildIndex = mStartBarrier->mDisplayList->mChildNodes.size() - 1;
+}
+
+#define SHADOW_DELTA 0.1f
+
+void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
+    auto& zChildren = mStartBarrier->mChildren;
+    SkASSERT(!zChildren.empty());
+
+    /**
+     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
+     * with very similar Z heights to draw together.
+     *
+     * 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.
+     */
+    size_t drawIndex = 0;
+
+    const size_t endIndex = zChildren.size();
+    while (drawIndex < endIndex     //draw only children with positive Z
+            && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON) drawIndex++;
+    size_t shadowIndex = drawIndex;
+
+    float lastCasterZ = 0.0f;
+    while (shadowIndex < endIndex || drawIndex < endIndex) {
+        if (shadowIndex < endIndex) {
+            const float casterZ = zChildren[shadowIndex]->getNodeProperties().getZ();
+
+            // 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) {
+                this->drawShadow(canvas, zChildren[shadowIndex]);
+                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
+                shadowIndex++;
+                continue;
+            }
+        }
+
+        RenderNodeDrawable* childNode = zChildren[drawIndex];
+        SkASSERT(childNode);
+        childNode->forceDraw(canvas);
+
+        drawIndex++;
+    }
+}
+
+/**
+ * @param canvas             the destination for the shadow draws
+ * @param shape              the shape casting the shadow
+ * @param casterZValue       the Z value of the caster RRect
+ * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
+ * @param draw               the function used to draw 'shape'
+ */
+template <typename Shape, typename F>
+static void DrawAmbientShadowGeneral(SkCanvas* canvas, const Shape& shape, float casterZValue,
+        float ambientAlpha, F&& draw) {
+    if (ambientAlpha <= 0) {
+        return;
+    }
+
+    const float kHeightFactor = 1.f/128.f;
+    const float kGeomFactor = 64;
+
+    float umbraAlpha = 1 / (1 + SkMaxScalar(casterZValue*kHeightFactor, 0));
+    float radius = casterZValue*kHeightFactor*kGeomFactor;
+
+    sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
+            SkBlurMask::ConvertRadiusToSigma(radius), SkBlurMaskFilter::kNone_BlurFlag);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setMaskFilter(std::move(mf));
+    paint.setARGB(ambientAlpha*umbraAlpha, 0, 0, 0);
+
+    draw(shape, paint);
+}
+
+/**
+ * @param canvas             the destination for the shadow draws
+ * @param shape              the shape casting the shadow
+ * @param casterZValue       the Z value of the caster RRect
+ * @param lightPos           the position of the light casting the shadow
+ * @param lightWidth
+ * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
+ * @param draw               the function used to draw 'shape'
+ */
+template <typename Shape, typename F>
+static void DrawSpotShadowGeneral(SkCanvas* canvas, const Shape& shape, float casterZValue,
+        float spotAlpha, F&& draw) {
+    if (spotAlpha <= 0) {
+        return;
+    }
+
+    const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
+    float zRatio = casterZValue / (lightPos.z - casterZValue);
+    // clamp
+    if (zRatio < 0.0f) {
+        zRatio = 0.0f;
+    } else if (zRatio > 0.95f) {
+        zRatio = 0.95f;
+    }
+
+    float blurRadius = SkiaFrameRenderer::getLightRadius()*zRatio;
+
+    SkAutoCanvasRestore acr(canvas, true);
+
+    sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
+            SkBlurMask::ConvertRadiusToSigma(blurRadius), SkBlurMaskFilter::kNone_BlurFlag);
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setMaskFilter(std::move(mf));
+    paint.setARGB(spotAlpha, 0, 0, 0);
+
+    // approximate projection by translating and scaling projected offset of bounds center
+    // TODO: compute the actual 2D projection
+    SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
+    canvas->scale(scale, scale);
+    SkPoint center = SkPoint::Make(shape.getBounds().centerX(), shape.getBounds().centerY());
+    SkMatrix ctmInverse;
+    if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+        ALOGW("Matrix is degenerate. Will not render shadow!");
+        return;
+    }
+    SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
+    ctmInverse.mapPoints(&lightPos2D, 1);
+    canvas->translate(zRatio*(center.fX - lightPos2D.fX), zRatio*(center.fY - lightPos2D.fY));
+
+    draw(shape, paint);
+}
+
+#define MAX_BLUR_RADIUS 16383.75f
+#define MAX_PAD         64
+
+/**
+ * @param casterRect         the rectangle bounds of the RRect casting the shadow
+ * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
+ * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
+ * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
+ * @param casterAlpha        the alpha value of the RRect casting the shadow (0.0-1.0 range)
+ * @param casterZValue       the Z value of the caster RRect
+ * @param scaleFactor        the scale needed to map from src-space to device-space
+ * @param canvas             the destination for the shadow draws
+ */
+static void DrawRRectShadows(const SkRect& casterRect, SkScalar casterCornerRadius,
+        SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue,
+        SkScalar scaleFactor, SkCanvas* canvas) {
+    SkASSERT(cornerRadius >= 0.0f);
+
+    // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
+    const SkScalar minRadius = 0.5f / scaleFactor;
+
+    const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()),
+            SkScalarHalf(casterRect.height()));
+    const bool isRect = casterCornerRadius <= minRadius;
+
+    sk_sp<SkShader> edgeShader(SkGaussianEdgeShader::Make());
+
+    if (ambientAlpha > 0.0f) {
+        static const float kHeightFactor = 1.0f / 128.0f;
+        static const float kGeomFactor = 64.0f;
+
+        SkScalar srcSpaceAmbientRadius = casterZValue * kHeightFactor * kGeomFactor;
+        // the device-space radius sent to the blur shader must fit in 14.2 fixed point
+        if (srcSpaceAmbientRadius*scaleFactor > MAX_BLUR_RADIUS) {
+            srcSpaceAmbientRadius = MAX_BLUR_RADIUS/scaleFactor;
+        }
+        const float umbraAlpha = 1.0f / (1.0f + std::max(casterZValue * kHeightFactor, 0.0f));
+        const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
+
+        // For the ambient rrect, we inset the offset rect by half the srcSpaceAmbientRadius
+        // to get our stroke shape.
+        SkScalar ambientPathOutset = std::max(ambientOffset - srcSpaceAmbientRadius * 0.5f,
+                minRadius);
+
+        SkRRect ambientRRect;
+        const SkRect temp = casterRect.makeOutset(ambientPathOutset, ambientPathOutset);
+        if (isOval) {
+            ambientRRect = SkRRect::MakeOval(temp);
+        } else if (isRect) {
+            ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset);
+        } else {
+            ambientRRect = SkRRect::MakeRectXY(temp, casterCornerRadius + ambientPathOutset,
+                    casterCornerRadius + ambientPathOutset);
+        }
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+        // we outset the stroke a little to cover up AA on the interior edge
+        float pad = 0.5f;
+        paint.setStrokeWidth(srcSpaceAmbientRadius + 2.0f * pad);
+        // handle scale of radius and pad due to CTM
+        pad *= scaleFactor;
+        const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
+        SkASSERT(devSpaceAmbientRadius <= MAX_BLUR_RADIUS);
+        SkASSERT(pad < MAX_PAD);
+        // convert devSpaceAmbientRadius to 14.2 fixed point and place in the R & G components
+        // convert pad to 6.2 fixed point and place in the B component
+        uint16_t iDevSpaceAmbientRadius = (uint16_t)(4.0f * devSpaceAmbientRadius);
+        paint.setColor(SkColorSetARGB((unsigned char) ambientAlpha, iDevSpaceAmbientRadius >> 8,
+                iDevSpaceAmbientRadius & 0xff, (unsigned char)(4.0f * pad)));
+
+        paint.setShader(edgeShader);
+        canvas->drawRRect(ambientRRect, paint);
+    }
+
+    if (spotAlpha > 0.0f) {
+        const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
+        float zRatio = casterZValue / (lightPos.z - casterZValue);
+        // clamp
+        if (zRatio < 0.0f) {
+            zRatio = 0.0f;
+        } else if (zRatio > 0.95f) {
+            zRatio = 0.95f;
+        }
+
+        const SkScalar lightWidth = SkiaFrameRenderer::getLightRadius();
+        SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
+        // the device-space radius sent to the blur shader must fit in 14.2 fixed point
+        if (srcSpaceSpotRadius*scaleFactor > MAX_BLUR_RADIUS) {
+            srcSpaceSpotRadius = MAX_BLUR_RADIUS/scaleFactor;
+        }
+
+        SkRRect spotRRect;
+        if (isOval) {
+            spotRRect = SkRRect::MakeOval(casterRect);
+        } else if (isRect) {
+            spotRRect = SkRRect::MakeRectXY(casterRect, minRadius, minRadius);
+        } else {
+            spotRRect = SkRRect::MakeRectXY(casterRect, casterCornerRadius, casterCornerRadius);
+        }
+
+        SkRRect spotShadowRRect;
+        // Compute the scale and translation for the spot shadow.
+        const SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
+        spotRRect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
+
+        SkPoint center = SkPoint::Make(spotShadowRRect.rect().centerX(),
+                                       spotShadowRRect.rect().centerY());
+        SkMatrix ctmInverse;
+        if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+            ALOGW("Matrix is degenerate. Will not render spot shadow!");
+            return;
+        }
+        SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
+        ctmInverse.mapPoints(&lightPos2D, 1);
+        const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
+                zRatio*(center.fY - lightPos2D.fY));
+
+        SkAutoCanvasRestore acr(canvas, true);
+
+        // We want to extend the stroked area in so that it meets up with the caster
+        // geometry. The stroked geometry will, by definition already be inset half the
+        // stroke width but we also have to account for the scaling.
+        // We also add 1/2 to cover up AA on the interior edge.
+        SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(casterRect.fLeft),
+                SkTAbs(casterRect.fRight)), SkTMax(SkTAbs(casterRect.fTop),
+                SkTAbs(casterRect.fBottom)));
+        SkScalar insetAmount = spotOffset.length() - (0.5f * srcSpaceSpotRadius) +
+                scaleOffset + 0.5f;
+
+        // Compute area
+        SkScalar strokeWidth = srcSpaceSpotRadius + insetAmount;
+        SkScalar strokedArea = 2.0f*strokeWidth * (spotShadowRRect.width()
+                + spotShadowRRect.height());
+        SkScalar filledArea = (spotShadowRRect.height() + srcSpaceSpotRadius)
+                * (spotShadowRRect.width() + srcSpaceSpotRadius);
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+
+        // If the area of the stroked geometry is larger than the fill geometry, just fill it.
+        if (strokedArea > filledArea || casterAlpha < 1.0f) {
+            paint.setStyle(SkPaint::kStrokeAndFill_Style);
+            paint.setStrokeWidth(srcSpaceSpotRadius);
+        } else {
+            // Since we can't have unequal strokes, inset the shadow rect so the inner
+            // and outer edges of the stroke will land where we want.
+            SkRect insetRect = spotShadowRRect.rect().makeInset(insetAmount/2.0f, insetAmount/2.0f);
+            SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount/2.0f,
+                    minRadius);
+            spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
+            paint.setStyle(SkPaint::kStroke_Style);
+            paint.setStrokeWidth(strokeWidth);
+        }
+
+        // handle scale of radius and pad due to CTM
+        const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
+        SkASSERT(devSpaceSpotRadius <= MAX_BLUR_RADIUS);
+
+        const SkScalar devSpaceSpotPad = 0;
+        SkASSERT(devSpaceSpotPad < MAX_PAD);
+
+        // convert devSpaceSpotRadius to 14.2 fixed point and place in the R & G
+        // components convert devSpaceSpotPad to 6.2 fixed point and place in the B component
+        uint16_t iDevSpaceSpotRadius = (uint16_t)(4.0f * devSpaceSpotRadius);
+        paint.setColor(SkColorSetARGB((unsigned char) spotAlpha, iDevSpaceSpotRadius >> 8,
+                iDevSpaceSpotRadius & 0xff, (unsigned char)(4.0f * devSpaceSpotPad)));
+        paint.setShader(edgeShader);
+
+        canvas->translate(spotOffset.fX, spotOffset.fY);
+        canvas->drawRRect(spotShadowRRect, paint);
+    }
+}
+
+/**
+ * @param casterRect         the rectangle bounds of the RRect casting the shadow
+ * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
+ * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
+ * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
+ * @param casterZValue       the Z value of the caster RRect
+ * @param scaleFactor        the scale needed to map from src-space to device-space
+ * @param clipRR             the oval or rect with which the drawn roundrect must be intersected
+ * @param canvas             the destination for the shadow draws
+ */
+static void DrawRRectShadowsWithClip(const SkRect& casterRect, SkScalar casterCornerRadius,
+        SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterZValue, SkScalar scaleFactor,
+        const SkRRect& clipRR, SkCanvas* canvas) {
+    SkASSERT(cornerRadius >= 0.0f);
+
+    const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()),
+            SkScalarHalf(casterRect.height()));
+
+    if (ambientAlpha > 0.0f) {
+        static const float kHeightFactor = 1.0f / 128.0f;
+        static const float kGeomFactor = 64.0f;
+
+        const SkScalar srcSpaceAmbientRadius = casterZValue * kHeightFactor * kGeomFactor;
+        const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
+
+        const float umbraAlpha = 1.0f / (1.0f + std::max(casterZValue * kHeightFactor, 0.0f));
+        const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
+
+        const SkRect srcSpaceAmbientRect = casterRect.makeOutset(ambientOffset, ambientOffset);
+        SkRect devSpaceAmbientRect;
+        canvas->getTotalMatrix().mapRect(&devSpaceAmbientRect, srcSpaceAmbientRect);
+
+        SkRRect devSpaceAmbientRRect;
+        if (isOval) {
+            devSpaceAmbientRRect = SkRRect::MakeOval(devSpaceAmbientRect);
+        } else {
+            const SkScalar devSpaceCornerRadius = scaleFactor * (casterCornerRadius + ambientOffset);
+            devSpaceAmbientRRect = SkRRect::MakeRectXY(devSpaceAmbientRect, devSpaceCornerRadius,
+                    devSpaceCornerRadius);
+        }
+
+        const SkRect srcSpaceAmbClipRect = clipRR.rect().makeOutset(ambientOffset, ambientOffset);
+        SkRect devSpaceAmbClipRect;
+        canvas->getTotalMatrix().mapRect(&devSpaceAmbClipRect, srcSpaceAmbClipRect);
+        SkRRect devSpaceAmbientClipRR;
+        if (clipRR.isOval()) {
+            devSpaceAmbientClipRR = SkRRect::MakeOval(devSpaceAmbClipRect);
+        } else {
+            SkASSERT(clipRR.isRect());
+            devSpaceAmbientClipRR = SkRRect::MakeRect(devSpaceAmbClipRect);
+        }
+
+        SkRect cover = srcSpaceAmbClipRect;
+        if (!cover.intersect(srcSpaceAmbientRect)) {
+            return;
+        }
+
+        SkPaint paint;
+        paint.setColor(SkColorSetARGB((unsigned char) ambientAlpha, 0, 0, 0));
+        paint.setShader(SkRRectsGaussianEdgeShader::Make(devSpaceAmbientRRect,
+                devSpaceAmbientClipRR, devSpaceAmbientRadius));
+        canvas->drawRect(cover, paint);
+    }
+
+    if (spotAlpha > 0.0f) {
+        const Vector3 lightPos = SkiaFrameRenderer::getLightCenter();
+        float zRatio = casterZValue / (lightPos.z - casterZValue);
+        // clamp
+        if (zRatio < 0.0f) {
+            zRatio = 0.0f;
+        } else if (zRatio > 0.95f) {
+            zRatio = 0.95f;
+        }
+
+        const SkScalar lightWidth = SkiaFrameRenderer::getLightRadius();
+        const SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
+        const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
+
+        // Compute the scale and translation for the spot shadow.
+        const SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
+        const SkMatrix spotMatrix = SkMatrix::MakeScale(scale, scale);
+
+        SkRect srcSpaceScaledRect = casterRect;
+        spotMatrix.mapRect(&srcSpaceScaledRect);
+        srcSpaceScaledRect.outset(SkScalarHalf(srcSpaceSpotRadius),
+                SkScalarHalf(srcSpaceSpotRadius));
+
+        SkRRect srcSpaceSpotRRect;
+        if (isOval) {
+            srcSpaceSpotRRect = SkRRect::MakeOval(srcSpaceScaledRect);
+        } else {
+            srcSpaceSpotRRect = SkRRect::MakeRectXY(srcSpaceScaledRect, casterCornerRadius * scale,
+                    casterCornerRadius * scale);
+        }
+
+        SkPoint center = SkPoint::Make(srcSpaceSpotRRect.rect().centerX(),
+                srcSpaceSpotRRect.rect().centerY());
+        SkMatrix ctmInverse;
+        if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+            ALOGW("Matrix is degenerate. Will not render spot shadow!");
+            return;
+        }
+        SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
+        ctmInverse.mapPoints(&lightPos2D, 1);
+        const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
+                zRatio*(center.fY - lightPos2D.fY));
+
+        SkAutoCanvasRestore acr(canvas, true);
+        canvas->translate(spotOffset.fX, spotOffset.fY);
+
+        SkRect devSpaceScaledRect;
+        canvas->getTotalMatrix().mapRect(&devSpaceScaledRect, srcSpaceScaledRect);
+
+        SkRRect devSpaceSpotRRect;
+        if (isOval) {
+            devSpaceSpotRRect = SkRRect::MakeOval(devSpaceScaledRect);
+        } else {
+            const SkScalar devSpaceScaledCornerRadius = casterCornerRadius * scale * scaleFactor;
+            devSpaceSpotRRect = SkRRect::MakeRectXY(devSpaceScaledRect, devSpaceScaledCornerRadius,
+                    devSpaceScaledCornerRadius);
+        }
+
+        SkPaint paint;
+        paint.setColor(SkColorSetARGB((unsigned char) spotAlpha, 0, 0, 0));
+
+        SkRect srcSpaceScaledClipRect = clipRR.rect();
+        spotMatrix.mapRect(&srcSpaceScaledClipRect);
+        srcSpaceScaledClipRect.outset(SkScalarHalf(srcSpaceSpotRadius),
+                SkScalarHalf(srcSpaceSpotRadius));
+
+        SkRect devSpaceScaledClipRect;
+        canvas->getTotalMatrix().mapRect(&devSpaceScaledClipRect, srcSpaceScaledClipRect);
+        SkRRect devSpaceSpotClipRR;
+        if (clipRR.isOval()) {
+            devSpaceSpotClipRR = SkRRect::MakeOval(devSpaceScaledClipRect);
+        } else {
+            SkASSERT(clipRR.isRect());
+            devSpaceSpotClipRR = SkRRect::MakeRect(devSpaceScaledClipRect);
+        }
+
+        paint.setShader(SkRRectsGaussianEdgeShader::Make(devSpaceSpotRRect, devSpaceSpotClipRR,
+                devSpaceSpotRadius));
+
+        SkRect cover = srcSpaceScaledClipRect;
+        if (!cover.intersect(srcSpaceSpotRRect.rect())) {
+            return;
+        }
+
+        canvas->drawRect(cover, paint);
+    }
+}
+
+/**
+ * @param casterRect         the rectangle bounds of the RRect casting the shadow
+ * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
+ * @param casterClipRect     a rectangular clip that must be intersected with the
+ *                           shadow-casting RRect prior to casting the shadow
+ * @param revealClip         a circular clip that must be interested with the castClipRect
+ *                           and the shadow-casting rect prior to casting the shadow
+ * @param ambientAlpha       the maximum alpha value to use when drawing the ambient shadow
+ * @param spotAlpha          the maximum alpha value to use when drawing the spot shadow
+ * @param casterAlpha        the alpha value of the RRect casting the shadow (0.0-1.0 range)
+ * @param casterZValue       the Z value of the caster RRect
+ * @param canvas             the destination for the shadow draws
+ *
+ * We have special cases for 4 round rect shadow draws:
+ *    1) a RRect clipped by a reveal animation
+ *    2) a RRect clipped by a rectangle
+ *    3) an unclipped RRect with non-uniform scale
+ *    4) an unclipped RRect with uniform scale
+ * 1,2 and 4 require that the scale is uniform.
+ * 1 and 2 require that rects stay rects.
+ */
+static bool DrawShadowsAsRRects(const SkRect& casterRect, SkScalar casterCornerRadius,
+        const SkRect& casterClipRect, const RevealClip& revealClip, SkScalar ambientAlpha,
+        SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue, SkCanvas* canvas) {
+    SkScalar scaleFactors[2];
+    if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
+        ALOGW("Matrix is degenerate. Will not render shadow!");
+        return false;
+    }
+
+    // The casterClipRect will contain the casterRect when bounds clipping is disabled
+    bool casterIsClippedByRect = !casterClipRect.contains(casterRect);
+    bool uniformScale = scaleFactors[0] == scaleFactors[1];
+
+    if (revealClip.willClip()) {
+        if (casterIsClippedByRect || !uniformScale || !canvas->getTotalMatrix().rectStaysRect()) {
+            return false;  // Fall back to the slow path since PathOps are required
+        }
+
+        const float revealRadius = revealClip.getRadius();
+        SkRect revealClipRect = SkRect::MakeLTRB(revealClip.getX()-revealRadius,
+                revealClip.getY()-revealRadius, revealClip.getX()+revealRadius,
+                revealClip.getY()+revealRadius);
+        SkRRect revealClipRR = SkRRect::MakeOval(revealClipRect);
+
+        DrawRRectShadowsWithClip(casterRect, casterCornerRadius, ambientAlpha, spotAlpha,
+                casterZValue, scaleFactors[0], revealClipRR, canvas);
+        return true;
+    }
+
+    if (casterIsClippedByRect) {
+        if (!uniformScale || !canvas->getTotalMatrix().rectStaysRect()) {
+            return false;  // Fall back to the slow path since PathOps are required
+        }
+
+        SkRRect casterClipRR = SkRRect::MakeRect(casterClipRect);
+
+        DrawRRectShadowsWithClip(casterRect, casterCornerRadius, ambientAlpha, spotAlpha,
+                casterZValue, scaleFactors[0], casterClipRR, canvas);
+        return true;
+    }
+
+    // The fast path needs uniform scale
+    if (!uniformScale) {
+        SkRRect casterRR = SkRRect::MakeRectXY(casterRect, casterCornerRadius, casterCornerRadius);
+        DrawAmbientShadowGeneral(canvas, casterRR, casterZValue, ambientAlpha,
+                [&](const SkRRect& rrect, const SkPaint& paint) {
+                    canvas->drawRRect(rrect, paint);
+                });
+        DrawSpotShadowGeneral(canvas, casterRR, casterZValue, spotAlpha,
+                [&](const SkRRect& rrect, const SkPaint& paint) {
+                canvas->drawRRect(rrect, paint);
+                });
+        return true;
+    }
+
+    DrawRRectShadows(casterRect, casterCornerRadius, ambientAlpha, spotAlpha, casterAlpha,
+            casterZValue, scaleFactors[0], canvas);
+    return true;
+}
+
+// copied from FrameBuilder::deferShadow
+void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) {
+    const RenderProperties& casterProperties = caster->getNodeProperties();
+
+    if (casterProperties.getAlpha() <= 0.0f
+            || casterProperties.getOutline().getAlpha() <= 0.0f
+            || !casterProperties.getOutline().getPath()
+            || casterProperties.getScaleX() == 0
+            || casterProperties.getScaleY() == 0) {
+        // no shadow to draw
+        return;
+    }
+
+    const SkScalar casterAlpha = casterProperties.getAlpha()
+            * casterProperties.getOutline().getAlpha();
+    if (casterAlpha <= 0.0f) {
+        return;
+    }
+
+    float ambientAlpha = SkiaFrameRenderer::getAmbientShadowAlpha()*casterAlpha;
+    float spotAlpha = SkiaFrameRenderer::getSpotShadowAlpha()*casterAlpha;
+    const float casterZValue = casterProperties.getZ();
+
+    const RevealClip& revealClip = casterProperties.getRevealClip();
+    const SkPath* revealClipPath = revealClip.getPath();
+    if (revealClipPath && revealClipPath->isEmpty()) {
+        // An empty reveal clip means nothing is drawn
+        return;
+    }
+
+    bool clippedToBounds = casterProperties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS;
+
+    SkRect casterClipRect = SkRect::MakeLargest();
+    if (clippedToBounds) {
+        Rect clipBounds;
+        casterProperties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
+        casterClipRect = clipBounds.toSkRect();
+    }
+
+    SkAutoCanvasRestore acr(canvas, true);
+
+    SkMatrix shadowMatrix;
+    mat4 hwuiMatrix(caster->getRecordedMatrix());
+    // TODO we don't pass the optional boolean to treat it as a 4x4 matrix
+    caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix);
+    hwuiMatrix.copyTo(shadowMatrix);
+    canvas->concat(shadowMatrix);
+
+    const Outline& casterOutline = casterProperties.getOutline();
+    Rect possibleRect;
+    float radius;
+    if (casterOutline.getAsRoundRect(&possibleRect, &radius)) {
+        if (DrawShadowsAsRRects(possibleRect.toSkRect(), radius, casterClipRect, revealClip,
+                ambientAlpha, spotAlpha, casterAlpha, casterZValue, canvas)) {
+            return;
+        }
+    }
+
+    // Hard cases and calls to general shadow code
+    const SkPath* casterOutlinePath = casterProperties.getOutline().getPath();
+
+    // holds temporary SkPath to store the result of intersections
+    SkPath tmpPath;
+    const SkPath* casterPath = casterOutlinePath;
+
+    // TODO: In to following course of code that calculates the final shape, is there an optimal
+    //       of doing the Op calculations?
+    // intersect the shadow-casting path with the reveal, if present
+    if (revealClipPath) {
+        Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath);
+        casterPath = &tmpPath;
+    }
+
+    // intersect the shadow-casting path with the clipBounds, if present
+    if (clippedToBounds) {
+        SkPath clipBoundsPath;
+        clipBoundsPath.addRect(casterClipRect);
+        Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, &tmpPath);
+        casterPath = &tmpPath;
+    }
+
+    DrawAmbientShadowGeneral(canvas, *casterPath, casterZValue, ambientAlpha,
+            [&](const SkPath& path, const SkPaint& paint) {
+                canvas->drawPath(path, paint);
+            });
+
+    DrawSpotShadowGeneral(canvas, *casterPath, casterZValue, spotAlpha,
+            [&](const SkPath& path, const SkPaint& paint) {
+                canvas->drawPath(path, paint);
+            });
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
new file mode 100644
index 0000000..298a732
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include "RenderNodeDrawable.h"
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+#include <utils/FatVector.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class SkiaDisplayList;
+class EndReorderBarrierDrawable;
+
+/**
+ * StartReorderBarrierDrawable and EndReorderBarrierDrawable work together to define
+ * a sub-list in a display list that need to be drawn out-of-order sorted instead by render
+ * node Z index.
+ * StartReorderBarrierDrawable will sort the entire range and it will draw
+ * render nodes in the range with negative Z index.
+ */
+class StartReorderBarrierDrawable : public SkDrawable {
+public:
+    explicit StartReorderBarrierDrawable(SkiaDisplayList* data);
+
+protected:
+    virtual SkRect onGetBounds() override {
+        return SkRect::MakeLargest();
+    }
+    virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+    size_t mEndChildIndex;
+    size_t mBeginChildIndex;
+    FatVector<RenderNodeDrawable*, 16> mChildren;
+    SkiaDisplayList* mDisplayList;
+
+    friend class EndReorderBarrierDrawable;
+};
+
+/**
+ * See StartReorderBarrierDrawable.
+ * EndReorderBarrierDrawable relies on StartReorderBarrierDrawable to host and sort the render
+ * nodes by Z index. When EndReorderBarrierDrawable is drawn it will draw all render nodes in the
+ * range with positive Z index. It is also responsible for drawing shadows for the nodes
+ * corresponding to their z-index.
+ */
+class EndReorderBarrierDrawable : public SkDrawable {
+public:
+    explicit EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier);
+protected:
+    virtual SkRect onGetBounds() override {
+        return SkRect::MakeLargest();
+    }
+    virtual void onDraw(SkCanvas* canvas) override;
+private:
+    void drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster);
+    StartReorderBarrierDrawable* mStartBarrier;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
similarity index 98%
rename from libs/hwui/SkiaDisplayList.cpp
rename to libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index d10f306..c734097e 100644
--- a/libs/hwui/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -24,6 +24,7 @@
 
 namespace android {
 namespace uirenderer {
+namespace skiapipeline {
 
 SkiaDisplayList::SkiaDisplayList(SkRect bounds) : mDrawable(SkLiteDL::New(bounds)) {
     SkASSERT(projectionReceiveIndex == -1);
@@ -130,5 +131,6 @@
     new (&allocator) LinearAllocator();
 }
 
+}; // namespace skiapipeline
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
similarity index 97%
rename from libs/hwui/SkiaDisplayList.h
rename to libs/hwui/pipeline/skia/SkiaDisplayList.h
index c8a82bd..734aae4a 100644
--- a/libs/hwui/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -17,7 +17,8 @@
 #pragma once
 
 #include "DisplayList.h"
-#include "SkiaDrawables.h"
+#include "GLFunctorDrawable.h"
+#include "RenderNodeDrawable.h"
 
 #include <deque>
 #include <SkLiteDL.h>
@@ -25,6 +26,7 @@
 
 namespace android {
 namespace uirenderer {
+namespace skiapipeline {
 
 /**
  * This class is intended to be self contained, but still subclasses from
@@ -148,5 +150,6 @@
     bool mPinnedImages = false;
 };
 
+}; // namespace skiapipeline
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaFrameRenderer.h b/libs/hwui/pipeline/skia/SkiaFrameRenderer.h
new file mode 100644
index 0000000..70207c1
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaFrameRenderer.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/**
+ * TODO: this is a stub that will be added in a subsquent CL
+ */
+class SkiaFrameRenderer {
+public:
+
+    static bool skpCaptureEnabled() { return false; }
+
+    // TODO avoids unused compile error but we need to pass this to the reorder drawables!
+    static float getLightRadius() {
+        return 1.0f;
+    }
+
+    static uint8_t getAmbientShadowAlpha() {
+        return 1;
+    }
+
+    static uint8_t getSpotShadowAlpha() {
+        return 1;
+    }
+
+    static Vector3 getLightCenter() {
+        Vector3 result;
+        result.x = result.y = result.z = 1.0f;
+        return result;
+    }
+
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
new file mode 100644
index 0000000..8a42983
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkiaRecordingCanvas.h"
+
+#include "Layer.h"
+#include "RenderNode.h"
+#include "LayerDrawable.h"
+#include "NinePatchUtils.h"
+#include "pipeline/skia/AnimatedDrawables.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+// ----------------------------------------------------------------------------
+// Recording Canvas Setup
+// ----------------------------------------------------------------------------
+
+void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, int width,
+        int height) {
+    mBarrierPending = false;
+    mCurrentBarrier = nullptr;
+    SkASSERT(mDisplayList.get() == nullptr);
+
+    if (renderNode) {
+        mDisplayList = renderNode->detachAvailableList();
+    }
+    SkRect bounds = SkRect::MakeWH(width, height);
+    if (mDisplayList) {
+        mDisplayList->reset(nullptr, bounds);
+    } else {
+        mDisplayList.reset(new SkiaDisplayList(bounds));
+    }
+
+    mRecorder.reset(mDisplayList->mDrawable.get());
+    SkiaCanvas::reset(&mRecorder);
+}
+
+uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() {
+    // close any existing chunks if necessary
+    insertReorderBarrier(false);
+    mRecorder.restoreToCount(1);
+    return mDisplayList.release();
+}
+
+// ----------------------------------------------------------------------------
+// Recording Canvas draw operations: View System
+// ----------------------------------------------------------------------------
+
+void SkiaRecordingCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
+        uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
+        uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
+        uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) {
+    drawDrawable(mDisplayList->allocateDrawable<AnimatedRoundRect>(left, top, right, bottom,
+            rx, ry, paint));
+}
+
+void SkiaRecordingCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x,
+        uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius,
+        uirenderer::CanvasPropertyPaint* paint) {
+    drawDrawable(mDisplayList->allocateDrawable<AnimatedCircle>(x, y, radius, paint));
+}
+
+void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) {
+    mBarrierPending = enableReorder;
+
+    if (nullptr != mCurrentBarrier) {
+        // finish off the existing chunk
+        SkDrawable* drawable =
+                mDisplayList->allocateDrawable<EndReorderBarrierDrawable>(
+                mCurrentBarrier);
+        mCurrentBarrier = nullptr;
+        drawDrawable(drawable);
+    }
+}
+
+void SkiaRecordingCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) {
+    if (layerUpdater != nullptr && layerUpdater->backingLayer() != nullptr) {
+        uirenderer::Layer* layer = layerUpdater->backingLayer();
+        sk_sp<SkDrawable> drawable(new LayerDrawable(layer));
+        drawDrawable(drawable.get());
+    }
+}
+
+void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
+    // lazily create the chunk if needed
+    if (mBarrierPending) {
+        mCurrentBarrier = (StartReorderBarrierDrawable*)
+                mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
+                mDisplayList.get());
+        drawDrawable(mCurrentBarrier);
+        mBarrierPending = false;
+    }
+
+    // record the child node
+    mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas());
+    drawDrawable(&mDisplayList->mChildNodes.back());
+
+    // use staging property, since recording on UI thread
+    if (renderNode->stagingProperties().isProjectionReceiver()) {
+        mDisplayList->mIsProjectionReceiver = true;
+        // set projectionReceiveIndex so that RenderNode.hasProjectionReceiver returns true
+        mDisplayList->projectionReceiveIndex = mDisplayList->mChildNodes.size() - 1;
+    }
+}
+
+void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
+        uirenderer::GlFunctorLifecycleListener* listener) {
+    mDisplayList->mChildFunctors.emplace_back(functor, listener, asSkCanvas());
+    drawDrawable(&mDisplayList->mChildFunctors.back());
+}
+
+class VectorDrawable : public SkDrawable {
+ public:
+    VectorDrawable(VectorDrawableRoot* tree) : mRoot(tree) {}
+
+ protected:
+     virtual SkRect onGetBounds() override {
+         return SkRect::MakeLargest();
+     }
+     virtual void onDraw(SkCanvas* canvas) override {
+         Bitmap& hwuiBitmap = mRoot->getBitmapUpdateIfDirty();
+         SkBitmap bitmap;
+         hwuiBitmap.getSkBitmap(&bitmap);
+         SkPaint* paint = mRoot->getPaint();
+         canvas->drawBitmapRect(bitmap, mRoot->mutateProperties()->getBounds(), paint);
+         /*
+          * TODO we can draw this directly but need to address the following...
+          *
+          * 1) Add drawDirect(SkCanvas*) to VectorDrawableRoot
+          * 2) fix VectorDrawable.cpp's Path::draw to not make a temporary path
+          *    so that we don't break caching
+          * 3) figure out how to set path's as volatile during animation
+          * 4) if mRoot->getPaint() != null either promote to layer (during
+          *    animation) or cache in SkSurface (for static content)
+          *
+          */
+     }
+
+ private:
+    sp<VectorDrawableRoot> mRoot;
+};
+
+void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
+    drawDrawable(mDisplayList->allocateDrawable<VectorDrawable>(tree));
+    mDisplayList->mVectorDrawables.push_back(tree);
+}
+
+// ----------------------------------------------------------------------------
+// Recording Canvas draw operations: Bitmaps
+// ----------------------------------------------------------------------------
+
+inline static const SkPaint* nonAAPaint(const SkPaint* origPaint, SkPaint* tmpPaint) {
+    if (origPaint && origPaint->isAntiAlias()) {
+        *tmpPaint = *origPaint;
+        tmpPaint->setAntiAlias(false);
+        return tmpPaint;
+    } else {
+        return origPaint;
+    }
+}
+
+void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
+    SkBitmap skBitmap;
+    bitmap.getSkBitmap(&skBitmap);
+
+    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
+    if (!skBitmap.isImmutable()) {
+        mDisplayList->mMutableImages.push_back(image.get());
+    }
+    SkPaint tmpPaint;
+    mRecorder.drawImage(image, left, top, nonAAPaint(paint, &tmpPaint));
+}
+
+void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix,
+        const SkPaint* paint) {
+    SkBitmap bitmap;
+    hwuiBitmap.getSkBitmap(&bitmap);
+    SkAutoCanvasRestore acr(&mRecorder, true);
+    concat(matrix);
+    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+    if (!bitmap.isImmutable()) {
+        mDisplayList->mMutableImages.push_back(image.get());
+    }
+    SkPaint tmpPaint;
+    mRecorder.drawImage(image, 0, 0, nonAAPaint(paint, &tmpPaint));
+}
+
+void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop,
+        float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight,
+        float dstBottom, const SkPaint* paint) {
+    SkBitmap bitmap;
+    hwuiBitmap.getSkBitmap(&bitmap);
+    SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
+    SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+    if (!bitmap.isImmutable()) {
+        mDisplayList->mMutableImages.push_back(image.get());
+    }
+    SkPaint tmpPaint;
+    mRecorder.drawImageRect(image, srcRect, dstRect, nonAAPaint(paint, &tmpPaint));
+}
+
+void SkiaRecordingCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk,
+        float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
+    SkBitmap bitmap;
+    hwuiBitmap.getSkBitmap(&bitmap);
+
+    SkCanvas::Lattice lattice;
+    NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height());
+
+    lattice.fFlags = nullptr;
+    int numFlags = 0;
+    if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) {
+        // We can expect the framework to give us a color for every distinct rect.
+        // Skia requires placeholder flags for degenerate rects.
+        numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
+    }
+
+    SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags);
+    if (numFlags > 0) {
+        NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk);
+    }
+
+    lattice.fBounds = nullptr;
+    SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+    if (!bitmap.isImmutable()) {
+        mDisplayList->mMutableImages.push_back(image.get());
+    }
+
+    SkPaint tmpPaint;
+    mRecorder.drawImageLattice(image.get(), lattice, dst, nonAAPaint(paint, &tmpPaint));
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
new file mode 100644
index 0000000..8aef97f
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#pragma once
+
+#include "SkiaCanvas.h"
+#include "SkiaDisplayList.h"
+#include "ReorderBarrierDrawables.h"
+#include <SkLiteRecorder.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/**
+ * A SkiaCanvas implementation that records drawing operations for deferred rendering backed by a
+ * SkLiteRecorder and a SkiaDisplayList.
+ */
+class SkiaRecordingCanvas : public SkiaCanvas {
+ public:
+    explicit SkiaRecordingCanvas(uirenderer::RenderNode* renderNode, int width, int height) {
+        initDisplayList(renderNode, width, height);
+    }
+
+    virtual void setBitmap(const SkBitmap& bitmap) override {
+        LOG_ALWAYS_FATAL("DisplayListCanvas is not backed by a bitmap.");
+    }
+
+    virtual void resetRecording(int width, int height,
+            uirenderer::RenderNode* renderNode) override {
+        initDisplayList(renderNode, width, height);
+    }
+
+    virtual uirenderer::DisplayList* finishRecording() override;
+
+    virtual void drawBitmap(Bitmap& bitmap, float left, float top,
+            const SkPaint* paint) override;
+    virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix,
+            const SkPaint* paint) override;
+    virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
+            float srcRight, float srcBottom, float dstLeft, float dstTop,
+            float dstRight, float dstBottom, const SkPaint* paint) override;
+    virtual void drawNinePatch(Bitmap& hwuiBitmap, const android::Res_png_9patch& chunk,
+            float dstLeft, float dstTop, float dstRight, float dstBottom,
+            const SkPaint* paint) override;
+
+    virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
+            uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
+            uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
+            uirenderer::CanvasPropertyPrimitive* ry,
+            uirenderer::CanvasPropertyPaint* paint) override;
+    virtual void drawCircle(uirenderer::CanvasPropertyPrimitive* x,
+            uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius,
+            uirenderer::CanvasPropertyPaint* paint) override;
+
+    virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
+
+    virtual void insertReorderBarrier(bool enableReorder) override;
+    virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
+    virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
+    virtual void callDrawGLFunction(Functor* functor,
+                                    uirenderer::GlFunctorLifecycleListener* listener) override;
+
+private:
+    SkLiteRecorder mRecorder;
+    std::unique_ptr<SkiaDisplayList> mDisplayList;
+    bool mBarrierPending;
+    StartReorderBarrierDrawable* mCurrentBarrier;
+
+    /**
+     *  A new SkiaDisplayList is created or recycled if available.
+     *
+     *  @param renderNode is optional and used to recycle an old display list.
+     *  @param width used to calculate recording bounds.
+     *  @param height used to calculate recording bounds.
+     */
+    void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height);
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 5b9b003..9530c79 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -125,5 +125,44 @@
     return utf16;
 }
 
+SkColor TestUtils::getColor(const sk_sp<SkSurface>& surface, int x, int y) {
+    SkPixmap pixmap;
+    if (!surface->peekPixels(&pixmap)) {
+        return 0;
+    }
+    switch (pixmap.colorType()) {
+        case kGray_8_SkColorType: {
+            const uint8_t* addr = pixmap.addr8(x, y);
+            return SkColorSetRGB(*addr, *addr, *addr);
+        }
+        case kAlpha_8_SkColorType: {
+            const uint8_t* addr = pixmap.addr8(x, y);
+            return SkColorSetA(0, addr[0]);
+        }
+        case kRGB_565_SkColorType: {
+            const uint16_t* addr = pixmap.addr16(x, y);
+            return SkPixel16ToColor(addr[0]);
+        }
+        case kARGB_4444_SkColorType: {
+            const uint16_t* addr = pixmap.addr16(x, y);
+            SkPMColor c = SkPixel4444ToPixel32(addr[0]);
+            return SkUnPreMultiply::PMColorToColor(c);
+        }
+        case kBGRA_8888_SkColorType: {
+            const uint32_t* addr = pixmap.addr32(x, y);
+            SkPMColor c = SkSwizzle_BGRA_to_PMColor(addr[0]);
+            return SkUnPreMultiply::PMColorToColor(c);
+        }
+        case kRGBA_8888_SkColorType: {
+            const uint32_t* addr = pixmap.addr32(x, y);
+            SkPMColor c = SkSwizzle_RGBA_to_PMColor(addr[0]);
+            return SkUnPreMultiply::PMColorToColor(c);
+        }
+        default:
+            return 0;
+    }
+    return 0;
+}
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index f6fe7d2..0be5a3b 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -260,6 +260,8 @@
          int mLastMode = -1;
      };
 
+    static SkColor getColor(const sk_sp<SkSurface>& surface, int x, int y);
+
 private:
     static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
         node->syncProperties();
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
new file mode 100644
index 0000000..19c311c
--- /dev/null
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <VectorDrawable.h>
+
+#include "AnimationContext.h"
+#include "DamageAccumulator.h"
+#include "IContextFactory.h"
+#include "pipeline/skia/SkiaDisplayList.h"
+#include "pipeline/skia/SkiaRecordingCanvas.h"
+#include "renderthread/CanvasContext.h"
+#include "tests/common/TestUtils.h"
+#include "SkiaCanvas.h"
+#include <SkLiteRecorder.h>
+#include <string.h>
+
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::skiapipeline;
+
+static sp<RenderNode> createSkiaNode(int left, int top, int right, int bottom,
+        std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup,
+        const char* name = nullptr, SkiaDisplayList* displayList = nullptr) {
+#if HWUI_NULL_GPU
+    // if RenderNodes are being sync'd/used, device info will be needed, since
+    // DeviceInfo::maxTextureSize() affects layer property
+    DeviceInfo::initialize();
+#endif
+    sp<RenderNode> node = new RenderNode();
+    if (name) {
+        node->setName(name);
+    }
+    RenderProperties& props = node->mutateStagingProperties();
+    props.setLeftTopRightBottom(left, top, right, bottom);
+    if (displayList) {
+        node->setStagingDisplayList(displayList, nullptr);
+    }
+    if (setup) {
+        std::unique_ptr<SkiaRecordingCanvas> canvas(new SkiaRecordingCanvas(nullptr,
+            props.getWidth(), props.getHeight()));
+        setup(props, *canvas.get());
+        node->setStagingDisplayList(canvas->finishRecording(), nullptr);
+    }
+    node->setPropertyFieldsDirty(0xFFFFFFFF);
+    TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+    return node;
+}
+
+TEST(RenderNodeDrawable, create) {
+    auto rootNode = TestUtils::createNode(0, 0, 200, 400,
+            [](RenderProperties& props, Canvas& canvas) {
+                canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
+            });
+
+    auto skLiteDL = SkLiteDL::New(SkRect::MakeWH(1, 1));
+    SkLiteRecorder canvas;
+    canvas.reset(skLiteDL.get());
+    canvas.translate(100, 100);
+    RenderNodeDrawable drawable(rootNode.get(), &canvas);
+
+    ASSERT_EQ(drawable.getRenderNode(), rootNode.get());
+    ASSERT_EQ(&drawable.getNodeProperties(), &rootNode->properties());
+    ASSERT_EQ(drawable.getRecordedMatrix(), canvas.getTotalMatrix());
+}
+
+TEST(RenderNodeDrawable, drawContent) {
+    auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+    SkCanvas& canvas = *surface->getCanvas();
+    canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+
+    //create a RenderNodeDrawable backed by a RenderNode backed by a SkLiteRecorder
+    auto rootNode = createSkiaNode(0, 0, 1, 1,
+        [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
+            recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+        });
+    RenderNodeDrawable drawable(rootNode.get(), &canvas, false);
+
+    //negative and positive Z order are drawn out of order
+    rootNode->animatorProperties().setElevation(10.0f);
+    canvas.drawDrawable(&drawable);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+    rootNode->animatorProperties().setElevation(-10.0f);
+    canvas.drawDrawable(&drawable);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+
+    //zero Z are drawn immediately
+    rootNode->animatorProperties().setElevation(0.0f);
+    canvas.drawDrawable(&drawable);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
+}
+
+//TODO: another test that verifies equal z values are drawn in order, and barriers prevent Z
+//intermixing (model after FrameBuilder zReorder)
+TEST(RenderNodeDrawable, drawAndReorder) {
+    //this test exercises StartReorderBarrierDrawable, EndReorderBarrierDrawable and
+    //SkiaRecordingCanvas
+    auto surface = SkSurface::MakeRasterN32Premul(4, 4);
+    SkCanvas& canvas = *surface->getCanvas();
+
+    canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
+
+    //-z draws to all 4 pixels (RED)
+    auto redNode = createSkiaNode(0, 0, 4, 4,
+        [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
+            redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+            props.setElevation(-10.0f);
+        }, "redNode");
+
+    //0z draws to bottom 2 pixels (GREEN)
+    auto bottomHalfGreenNode = createSkiaNode(0, 0, 4, 4,
+            [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
+                SkPaint greenPaint;
+                greenPaint.setColor(SK_ColorGREEN);
+                greenPaint.setStyle(SkPaint::kFill_Style);
+                bottomHalfGreenCanvas.drawRect(0, 2, 4, 4, greenPaint);
+                props.setElevation(0.0f);
+            }, "bottomHalfGreenNode");
+
+    //+z draws to right 2 pixels (BLUE)
+    auto rightHalfBlueNode = createSkiaNode(0, 0, 4, 4,
+        [](RenderProperties& props, SkiaRecordingCanvas& rightHalfBlueCanvas) {
+            SkPaint bluePaint;
+            bluePaint.setColor(SK_ColorBLUE);
+            bluePaint.setStyle(SkPaint::kFill_Style);
+            rightHalfBlueCanvas.drawRect(2, 0, 4, 4, bluePaint);
+            props.setElevation(10.0f);
+        }, "rightHalfBlueNode");
+
+    auto rootNode = createSkiaNode(0, 0, 4, 4,
+            [&](RenderProperties& props, SkiaRecordingCanvas& rootRecorder) {
+                rootRecorder.insertReorderBarrier(true);
+                //draw in reverse Z order, so Z alters draw order
+                rootRecorder.drawRenderNode(rightHalfBlueNode.get());
+                rootRecorder.drawRenderNode(bottomHalfGreenNode.get());
+                rootRecorder.drawRenderNode(redNode.get());
+            }, "rootNode");
+
+    RenderNodeDrawable drawable3(rootNode.get(), &canvas, false);
+    canvas.drawDrawable(&drawable3);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 3), SK_ColorGREEN);
+    ASSERT_EQ(TestUtils::getColor(surface, 3, 3), SK_ColorBLUE);
+}
+
+TEST(RenderNodeDrawable, composeOnLayer)
+{
+    auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+    SkCanvas& canvas = *surface->getCanvas();
+    canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+
+    auto rootNode = createSkiaNode(0, 0, 1, 1,
+        [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
+            recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+        });
+
+    //attach a layer to the render node
+    auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1);
+    auto canvas2 = surfaceLayer->getCanvas();
+    canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+    rootNode->setLayerSurface( surfaceLayer  );
+
+    RenderNodeDrawable drawable1(rootNode.get(), &canvas, false);
+    canvas.drawDrawable(&drawable1);
+    ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
+
+    RenderNodeDrawable drawable2(rootNode.get(), &canvas, true);
+    canvas.drawDrawable(&drawable2);
+    ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0));
+
+    RenderNodeDrawable drawable3(rootNode.get(), &canvas, false);
+    canvas.drawDrawable(&drawable3);
+    ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
+
+    rootNode->setLayerSurface( sk_sp<SkSurface>()  );
+}
+
+//TODO: refactor to cover test cases from FrameBuilderTests_projectionReorder
+//validate with bounds and projection path mask.
+//TODO: research if we could hook in and mock/validate different aspects of the drawing,
+//instead of validating pixels
+TEST(RenderNodeDrawable, projectDraw) {
+    auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+    SkCanvas& canvas = *surface->getCanvas();
+    canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+
+    auto redNode = createSkiaNode(0, 0, 1, 1,
+        [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
+            redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+        }, "redNode");
+
+    auto greenNodeWithRedChild = createSkiaNode(0, 0, 1, 1,
+        [&](RenderProperties& props, SkiaRecordingCanvas& greenCanvasWithRedChild) {
+            greenCanvasWithRedChild.drawRenderNode(redNode.get());
+            greenCanvasWithRedChild.drawColor(SK_ColorGREEN, SkBlendMode::kSrcOver);
+        }, "greenNodeWithRedChild");
+
+    auto rootNode = createSkiaNode(0, 0, 1, 1,
+        [&](RenderProperties& props, SkiaRecordingCanvas& rootCanvas) {
+            rootCanvas.drawRenderNode(greenNodeWithRedChild.get());
+        }, "rootNode");
+    SkiaDisplayList* rootDisplayList = static_cast<SkiaDisplayList*>(
+        (const_cast<DisplayList*>(rootNode->getDisplayList())));
+
+    RenderNodeDrawable rootDrawable(rootNode.get(), &canvas, false);
+    canvas.drawDrawable(&rootDrawable);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorGREEN);
+
+    //project redNode on rootNode, which will change the test outcome,
+    //because redNode will draw after greenNodeWithRedChild
+    rootDisplayList->mIsProjectionReceiver = true;
+    redNode->animatorProperties().setProjectBackwards(true);
+    canvas.drawDrawable(&rootDrawable);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
+}
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 72029b9..fe6cea6 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -20,14 +20,14 @@
 #include "AnimationContext.h"
 #include "DamageAccumulator.h"
 #include "IContextFactory.h"
-#include "SkiaDisplayList.h"
+#include "pipeline/skia/SkiaDisplayList.h"
 #include "renderthread/CanvasContext.h"
 #include "tests/common/TestUtils.h"
 
-
 using namespace android;
 using namespace android::uirenderer;
 using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::skiapipeline;
 
 TEST(SkiaDisplayList, create) {
     SkRect bounds = SkRect::MakeWH(200, 200);
diff --git a/media/tests/players/Android.mk b/media/tests/players/Android.mk
index 7ab6458..35aba4d 100644
--- a/media/tests/players/Android.mk
+++ b/media/tests/players/Android.mk
@@ -20,6 +20,7 @@
 
 LOCAL_SHARED_LIBRARIES:= \
     libbinder \
+    libmedia \
     libutils \
     liblog
 
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 662a1cd..4e6f3d0 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -163,7 +163,8 @@
                     final VolumeInfo privateVol = mStorageManager.findPrivateForEmulated(volume);
                     title = mStorageManager.getBestVolumeDescription(privateVol);
                 }
-            } else if (volume.getType() == VolumeInfo.TYPE_PUBLIC) {
+            } else if (volume.getType() == VolumeInfo.TYPE_PUBLIC
+                    && volume.getMountUserId() == userId) {
                 rootId = volume.getFsUuid();
                 title = mStorageManager.getBestVolumeDescription(volume);
             } else {
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index caad889..58aab1d 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -91,7 +91,7 @@
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Point d\'accès Wi-Fi mobile"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Via Bluetooth"</string>
     <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Partage de connexion"</string>
-    <string name="tether_settings_title_all" msgid="8356136101061143841">"Partage de connexion et point d\'accès mobile"</string>
+    <string name="tether_settings_title_all" msgid="8356136101061143841">"Partage de connexion"</string>
     <string name="managed_user_title" msgid="8109605045406748842">"Toutes les applis profess."</string>
     <string name="user_guest" msgid="8475274842845401871">"Invité"</string>
     <string name="unknown" msgid="1592123443519355854">"Inconnu"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 78e1ee1..5dcad7a 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -132,10 +132,10 @@
     <item msgid="8903157781070679765">"Normaal"</item>
     <item msgid="164347302621392996">"Snel"</item>
     <item msgid="5794028588101562009">"Sneller"</item>
-    <item msgid="7163942783888652942">"Zeer snel"</item>
-    <item msgid="7831712693748700507">"Vlug"</item>
-    <item msgid="5194774745031751806">"Zeer vlug"</item>
-    <item msgid="9085102246155045744">"Snelst"</item>
+    <item msgid="7163942783888652942">"Nog sneller"</item>
+    <item msgid="7831712693748700507">"Heel erg snel"</item>
+    <item msgid="5194774745031751806">"Snelst"</item>
+    <item msgid="9085102246155045744">"Allerallersnelst"</item>
   </string-array>
     <string name="choose_profile" msgid="8229363046053568878">"Profiel kiezen"</string>
     <string name="category_personal" msgid="1299663247844969448">"Persoonlijk"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
index b04948b..ebb5d19 100644
--- a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
@@ -82,11 +82,10 @@
 
         final String PROC_VERSION_REGEX =
                 "Linux version (\\S+) " + /* group 1: "3.0.31-g6fb96c9" */
-                "\\((\\S+?)\\) " +        /* group 2: "x@y.com" (kernel builder) */
-                "(?:\\(gcc.+? \\)) " +    /* ignore: GCC version information */
-                "(#\\d+) " +              /* group 3: "#1" */
-                "(?:.*?)?" +              /* ignore: optional SMP, PREEMPT, and any CONFIG_FLAGS */
-                "((Sun|Mon|Tue|Wed|Thu|Fri|Sat).+)"; /* group 4: "Thu Jun 28 11:02:39 PDT 2012" */
+                "\\((\\S+)\\)" +          /* group 2: "x@y.com" (kernel builder) */
+                ".*(#\\d+)" +             /* group 3: "#1" */
+                /* group 4: "Thu Jun 28 11:02:39 PDT 2012" */
+                ".*((?:Sun|Mon|Tue|Wed|Thu|Fri|Sat).+)";
 
         Matcher m = Pattern.compile(PROC_VERSION_REGEX).matcher(rawKernelVersion);
         if (!m.matches()) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java b/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
index 4e85f2e..512049f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
@@ -26,10 +26,15 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.Log;
+
+import java.util.List;
 
 public class ProfileSelectDialog extends DialogFragment implements OnClickListener {
 
+    private static final String TAG = "ProfileSelectDialog";
     private static final String ARG_SELECTED_TILE = "selectedTile";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private Tile mSelectedTile;
 
@@ -68,4 +73,20 @@
         getActivity().startActivityAsUser(mSelectedTile.intent, user);
         ((SettingsDrawerActivity) getActivity()).onProfileTileOpen();
     }
+
+    public static void updateUserHandlesIfNeeded(Context context, Tile tile) {
+        List<UserHandle> userHandles = tile.userHandle;
+        if (tile.userHandle == null || tile.userHandle.size() <= 1) {
+            return;
+        }
+        final UserManager userManager = UserManager.get(context);
+        for (int i = userHandles.size() - 1; i >= 0; i--) {
+            if (userManager.getUserInfo(userHandles.get(i).getIdentifier()) == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "Delete the user: " + userHandles.get(i).getIdentifier());
+                }
+                userHandles.remove(i);
+            }
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index bad7ba4..86514dc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -28,10 +28,8 @@
 import android.content.res.TypedArray;
 import android.os.AsyncTask;
 import android.os.Bundle;
-import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.support.annotation.VisibleForTesting;
 import android.support.v4.widget.DrawerLayout;
 import android.util.ArraySet;
 import android.util.Log;
@@ -312,7 +310,7 @@
             return true;
         }
         try {
-            updateUserHandlesIfNeeded(tile);
+            ProfileSelectDialog.updateUserHandlesIfNeeded(this /* context */, tile);
             int numUserHandles = tile.userHandle.size();
             if (numUserHandles > 1) {
                 ProfileSelectDialog.show(getFragmentManager(), tile);
@@ -334,24 +332,6 @@
         return true;
     }
 
-    private void updateUserHandlesIfNeeded(Tile tile) {
-        List<UserHandle> userHandles = tile.userHandle;
-
-        for (int i = userHandles.size() - 1; i >= 0; i--) {
-            if (mUserManager.getUserInfo(userHandles.get(i).getIdentifier()) == null) {
-                if (DEBUG) {
-                    Log.d(TAG, "Delete the user: " + userHandles.get(i).getIdentifier());
-                }
-                userHandles.remove(i);
-            }
-        }
-    }
-
-    @VisibleForTesting
-    public void setUserManager(UserManager userManager) {
-        mUserManager = userManager;
-    }
-
     protected void onTileClicked(Tile tile) {
         if (openTile(tile)) {
             finish();
diff --git a/packages/SettingsLib/tests/src/com/android/settingslib/drawer/ProfileSelectDialogTest.java b/packages/SettingsLib/tests/src/com/android/settingslib/drawer/ProfileSelectDialogTest.java
new file mode 100644
index 0000000..ac2d759
--- /dev/null
+++ b/packages/SettingsLib/tests/src/com/android/settingslib/drawer/ProfileSelectDialogTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 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.settingslib.drawer;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static junit.framework.Assert.assertEquals;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ProfileSelectDialogTest {
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private UserManager mUserManager;
+    private static final UserHandle NORMAL_USER = UserHandle.of(1111);
+    private static final UserHandle REMOVED_USER = UserHandle.of(2222);
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        final UserInfo userInfo = new UserInfo(
+                NORMAL_USER.getIdentifier(), "test_user", UserInfo.FLAG_RESTRICTED);
+        when(mUserManager.getUserInfo(NORMAL_USER.getIdentifier())).thenReturn(userInfo);
+    }
+
+    @Test
+    public void testUpdateUserHandlesIfNeeded_Normal() {
+        final Tile tile = new Tile();
+        tile.intent = new Intent();
+        tile.userHandle.add(NORMAL_USER);
+
+        ProfileSelectDialog.updateUserHandlesIfNeeded(mContext, tile);
+
+        assertEquals(tile.userHandle.size(), 1);
+        assertEquals(tile.userHandle.get(0).getIdentifier(), NORMAL_USER.getIdentifier());
+        verify(mUserManager, never()).getUserInfo(NORMAL_USER.getIdentifier());
+    }
+
+    @Test
+    public void testUpdateUserHandlesIfNeeded_Remove() {
+        final Tile tile = new Tile();
+        tile.intent = new Intent();
+        tile.userHandle.add(REMOVED_USER);
+        tile.userHandle.add(NORMAL_USER);
+        tile.userHandle.add(REMOVED_USER);
+
+        ProfileSelectDialog.updateUserHandlesIfNeeded(mContext, tile);
+
+        assertEquals(tile.userHandle.size(), 1);
+        assertEquals(tile.userHandle.get(0).getIdentifier(), NORMAL_USER.getIdentifier());
+        verify(mUserManager, times(1)).getUserInfo(NORMAL_USER.getIdentifier());
+        verify(mUserManager, times(2)).getUserInfo(REMOVED_USER.getIdentifier());
+    }
+}
diff --git a/packages/SettingsLib/tests/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java b/packages/SettingsLib/tests/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
index 4d7d4cf..1e87ea0f 100644
--- a/packages/SettingsLib/tests/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
+++ b/packages/SettingsLib/tests/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
@@ -19,25 +19,16 @@
 import android.app.Instrumentation;
 import android.content.Intent;
 import android.support.test.InstrumentationRegistry;
-import android.annotation.Nullable;
-import android.content.Intent;
-import android.content.pm.UserInfo;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.UserManager;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 
-import com.android.settingslib.drawer.SettingsDrawerActivity;
-import com.android.settingslib.drawer.Tile;
 import com.android.settingslib.R;
 
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import static android.support.test.espresso.Espresso.onView;
@@ -45,66 +36,19 @@
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
-import static junit.framework.Assert.assertEquals;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.verify;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class SettingsDrawerActivityTest {
-    @Mock
-    private UserManager mUserManager;
+
 
     @Rule
     public ActivityTestRule<TestActivity> mActivityRule =
             new ActivityTestRule<>(TestActivity.class, true, true);
 
-    private static final UserHandle NORMAL_USER = UserHandle.of(1111);
-    private static final UserHandle REMOVED_USER = UserHandle.of(2222);
-
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-
-        final UserInfo userInfo = new UserInfo(
-                NORMAL_USER.getIdentifier(), "test_user", UserInfo.FLAG_RESTRICTED);
-        when(mUserManager.getUserInfo(NORMAL_USER.getIdentifier())).thenReturn(userInfo);
-    }
-
-    @Test
-    public void testUpdateUserHandlesIfNeeded_Normal() {
-        TestActivity activity = mActivityRule.getActivity();
-        activity.setUserManager(mUserManager);
-
-        Tile tile = new Tile();
-        tile.intent = new Intent();
-        tile.userHandle.add(NORMAL_USER);
-
-        activity.openTile(tile);
-
-        assertEquals(tile.userHandle.size(), 1);
-        assertEquals(tile.userHandle.get(0).getIdentifier(), NORMAL_USER.getIdentifier());
-        verify(mUserManager, times(1)).getUserInfo(NORMAL_USER.getIdentifier());
-    }
-
-    @Test
-    public void testUpdateUserHandlesIfNeeded_Remove() {
-        TestActivity activity = mActivityRule.getActivity();
-        activity.setUserManager(mUserManager);
-
-        Tile tile = new Tile();
-        tile.intent = new Intent();
-        tile.userHandle.add(REMOVED_USER);
-        tile.userHandle.add(NORMAL_USER);
-        tile.userHandle.add(REMOVED_USER);
-
-        activity.openTile(tile);
-
-        assertEquals(tile.userHandle.size(), 1);
-        assertEquals(tile.userHandle.get(0).getIdentifier(), NORMAL_USER.getIdentifier());
-        verify(mUserManager, times(1)).getUserInfo(NORMAL_USER.getIdentifier());
-        verify(mUserManager, times(2)).getUserInfo(REMOVED_USER.getIdentifier());
     }
 
     @Test
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1b7a922..d8c8b82 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1667,6 +1667,14 @@
          not appear on production builds ever. -->
     <string name="plugins" translatable="false">Plugins</string>
 
+    <!-- Ambient display section of the tuner. Non-translatable since it should
+        not appear on production builds ever. -->
+    <string name="tuner_doze" translatable="false">Ambient Display</string>
+
+    <!-- Ambient display always-on of the tuner. Non-translatable since it should
+        not appear on production builds ever. -->
+    <string name="tuner_doze_always_on" translatable="false">Always on</string>
+
     <!-- PIP section of the tuner. Non-translatable since it should
         not appear on production builds ever. -->
     <string name="picture_in_picture" translatable="false">Picture-in-Picture</string>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 46a9ee3..942f847 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -139,6 +139,17 @@
 
     </PreferenceScreen>
 
+    <PreferenceScreen
+      android:key="doze"
+      android:title="@string/tuner_doze">
+
+        <com.android.systemui.tuner.TunerSwitch
+          android:key="doze_always_on"
+          android:title="@string/tuner_doze_always_on"
+          sysui:defValue="false" />
+
+    </PreferenceScreen>
+
     <!--
     <Preference
         android:key="nav_bar"
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 82a1bfe..02a98b0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -27,6 +27,7 @@
     void startDozing(@NonNull Runnable ready);
     void pulseWhileDozing(@NonNull PulseCallback callback, int reason);
     void stopDozing();
+    void dozeTimeTick();
     boolean isPowerSaveActive();
     boolean isNotificationLightOn();
     boolean isPulsingBlocked();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 8789845c..cbaf232 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.doze;
 
+import android.app.AlarmManager;
 import android.app.UiModeManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -43,7 +44,9 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Calendar;
 import java.util.Date;
+import java.util.GregorianCalendar;
 
 public class DozeService extends DreamService implements DozeSensors.Callback {
     private static final String TAG = "DozeService";
@@ -77,6 +80,7 @@
     private long mNotificationPulseTime;
 
     private AmbientDisplayConfiguration mConfig;
+    private AlarmManager mAlarmManager;
 
     public DozeService() {
         if (DEBUG) Log.d(mTag, "new DozeService()");
@@ -114,6 +118,7 @@
         setWindowless(true);
 
         mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+        mAlarmManager = (AlarmManager) mContext.getSystemService(AlarmManager.class);
         mConfig = new AmbientDisplayConfiguration(mContext);
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
@@ -168,6 +173,10 @@
                 // stopDozing because can we just keep dozing until the bitter end.
             }
         }));
+
+        if (mDozeParameters.getAlwaysOn()) {
+            mTimeTick.onAlarm();
+        }
     }
 
     @Override
@@ -184,6 +193,7 @@
 
         // Tell the host that it's over.
         mHost.stopDozing();
+        mAlarmManager.cancel(mTimeTick);
     }
 
     private void requestPulse(final int reason) {
@@ -264,7 +274,11 @@
 
     private void turnDisplayOff() {
         if (DEBUG) Log.d(mTag, "Display off");
-        setDozeScreenState(Display.STATE_OFF);
+        if (mDozeParameters.getAlwaysOn()) {
+            turnDisplayOn();
+        } else {
+            setDozeScreenState(Display.STATE_OFF);
+        }
     }
 
     private void turnDisplayOn() {
@@ -353,6 +367,26 @@
         }
     };
 
+    private AlarmManager.OnAlarmListener mTimeTick = new AlarmManager.OnAlarmListener() {
+        @Override
+        public void onAlarm() {
+            mHost.dozeTimeTick();
+
+            // Keep wakelock until a frame has been pushed.
+            mHandler.post(mWakeLock.wrap(()->{}));
+
+            Calendar calendar = GregorianCalendar.getInstance();
+            calendar.setTimeInMillis(System.currentTimeMillis());
+            calendar.set(Calendar.MILLISECOND, 0);
+            calendar.set(Calendar.SECOND, 0);
+            calendar.add(Calendar.MINUTE, 1);
+
+            long delta = calendar.getTimeInMillis() - System.currentTimeMillis();
+            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    SystemClock.elapsedRealtime() + delta, "doze_time_tick", mTimeTick, mHandler);
+        }
+    };
+
     private final DozeHost.Callback mHostCallback = new DozeHost.Callback() {
         @Override
         public void onNewNotifications() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index ffe99c5..7f2d415 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -17,15 +17,16 @@
 package com.android.systemui.pip.phone;
 
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 
 import static com.android.systemui.Interpolators.FAST_OUT_LINEAR_IN;
 import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.systemui.recents.misc.Utilities.RECT_EVALUATOR;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.ActivityManager.StackInfo;
 import android.app.IActivityManager;
 import android.content.Context;
@@ -34,6 +35,8 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.IPinnedStackController;
+import android.view.IPinnedStackListener;
 import android.view.IWindowManager;
 import android.view.InputChannel;
 import android.view.InputEvent;
@@ -41,9 +44,9 @@
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
-import android.view.animation.Interpolator;
 
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.policy.PipMotionHelper;
 import com.android.internal.policy.PipSnapAlgorithm;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.tuner.TunerService;
@@ -65,12 +68,16 @@
 
     private final Context mContext;
     private final IActivityManager mActivityManager;
+    private final IWindowManager mWindowManager;
     private final ViewConfiguration mViewConfig;
     private final InputChannel mInputChannel = new InputChannel();
+    private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
+    private IPinnedStackController mPinnedStackController;
 
     private final PipInputEventReceiver mInputEventReceiver;
     private PipDismissViewController mDismissViewController;
     private PipSnapAlgorithm mSnapAlgorithm;
+    private PipMotionHelper mMotionHelper;
 
     private boolean mEnableSwipeToDismiss = true;
     private boolean mEnableDragToDismiss = true;
@@ -78,6 +85,13 @@
     private final Rect mPinnedStackBounds = new Rect();
     private final Rect mBoundedPinnedStackBounds = new Rect();
     private ValueAnimator mPinnedStackBoundsAnimator = null;
+    private ValueAnimator.AnimatorUpdateListener mUpdatePinnedStackBoundsListener =
+            new AnimatorUpdateListener() {
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            mPinnedStackBounds.set((Rect) animation.getAnimatedValue());
+        }
+    };
 
     private final PointF mDownTouch = new PointF();
     private final PointF mLastTouch = new PointF();
@@ -88,10 +102,13 @@
     private final FlingAnimationUtils mFlingAnimationUtils;
     private VelocityTracker mVelocityTracker;
 
+    private final Rect mTmpBounds = new Rect();
+
     /**
      * Input handler used for Pip windows.
      */
     private final class PipInputEventReceiver extends InputEventReceiver {
+
         public PipInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
         }
@@ -111,6 +128,22 @@
         }
     }
 
+    /**
+     * Handler for messages from the PIP controller.
+     */
+    private class PinnedStackListener extends IPinnedStackListener.Stub {
+
+        @Override
+        public void onListenerRegistered(IPinnedStackController controller) {
+            mPinnedStackController = controller;
+        }
+
+        @Override
+        public void onBoundsChanged(boolean adjustedForIme) {
+            // Do nothing
+        }
+    }
+
     public PipTouchHandler(Context context, IActivityManager activityManager,
             IWindowManager windowManager) {
 
@@ -118,17 +151,20 @@
         try {
             windowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
             windowManager.createInputConsumer(INPUT_CONSUMER_PIP, mInputChannel);
+            windowManager.registerPinnedStackListener(DEFAULT_DISPLAY, mPinnedStackListener);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to create PIP input consumer", e);
         }
         mContext = context;
         mActivityManager = activityManager;
+        mWindowManager = windowManager;
         mViewConfig = ViewConfiguration.get(context);
         mInputEventReceiver = new PipInputEventReceiver(mInputChannel, Looper.myLooper());
         if (mEnableDragToDismiss) {
             mDismissViewController = new PipDismissViewController(context);
         }
         mFlingAnimationUtils = new FlingAnimationUtils(context, 2f);
+        mMotionHelper = new PipMotionHelper(BackgroundThread.getHandler());
 
         // Register any tuner settings changes
         TunerService.get(context).addTunable(this, TUNER_KEY_SWIPE_TO_DISMISS,
@@ -137,12 +173,15 @@
 
     @Override
     public void onTuningChanged(String key, String newValue) {
+        if (newValue == null) {
+            return;
+        }
         switch (key) {
             case TUNER_KEY_SWIPE_TO_DISMISS:
-                mEnableSwipeToDismiss = (newValue != null) && Integer.parseInt(newValue) != 0;
+                mEnableSwipeToDismiss = Integer.parseInt(newValue) != 0;
                 break;
             case TUNER_KEY_DRAG_TO_DISMISS:
-                mEnableDragToDismiss = (newValue != null) && Integer.parseInt(newValue) != 0;
+                mEnableDragToDismiss = Integer.parseInt(newValue) != 0;
                 break;
         }
     }
@@ -152,6 +191,11 @@
     }
 
     private void handleTouchEvent(MotionEvent ev) {
+        // Skip touch handling until we are bound to the controller
+        if (mPinnedStackController == null) {
+            return;
+        }
+
         switch (ev.getAction()) {
             case MotionEvent.ACTION_DOWN: {
                 // Cancel any existing animations on the pinned stack
@@ -166,6 +210,11 @@
                 mLastTouch.set(ev.getX(), ev.getY());
                 mDownTouch.set(mLastTouch);
                 mIsDragging = false;
+                try {
+                    mPinnedStackController.setInInteractiveMode(true);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Could not set dragging state", e);
+                }
                 if (mEnableDragToDismiss) {
                     // TODO: Consider setting a timer such at after X time, we show the dismiss
                     //       target if the user hasn't already dragged some distance
@@ -203,8 +252,12 @@
 
                 if (mIsSwipingToDismiss) {
                     // Ignore the vertical movement
-                    top = mPinnedStackBounds.top;
-                    movePinnedStack(left, top);
+                    mTmpBounds.set(mPinnedStackBounds);
+                    mTmpBounds.offsetTo((int) left, mPinnedStackBounds.top);
+                    if (!mTmpBounds.equals(mPinnedStackBounds)) {
+                        mPinnedStackBounds.set(mTmpBounds);
+                        mMotionHelper.resizeToBounds(mPinnedStackBounds);
+                    }
                 } else if (mIsDragging) {
                     // Move the pinned stack
                     if (!DEBUG_ALLOW_OUT_OF_BOUNDS_STACK) {
@@ -213,7 +266,12 @@
                         top = Math.max(mBoundedPinnedStackBounds.top, Math.min(
                                 mBoundedPinnedStackBounds.bottom, top));
                     }
-                    movePinnedStack(left, top);
+                    mTmpBounds.set(mPinnedStackBounds);
+                    mTmpBounds.offsetTo((int) left, (int) top);
+                    if (!mTmpBounds.equals(mPinnedStackBounds)) {
+                        mPinnedStackBounds.set(mTmpBounds);
+                        mMotionHelper.resizeToBounds(mPinnedStackBounds);
+                    }
                 }
                 mLastTouch.set(ev.getX(), ev.getY());
                 break;
@@ -275,6 +333,11 @@
             case MotionEvent.ACTION_CANCEL: {
                 mIsDragging = false;
                 mIsSwipingToDismiss = false;
+                try {
+                    mPinnedStackController.setInInteractiveMode(false);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Could not set dragging state", e);
+                }
                 recycleVelocityTracker();
                 break;
             }
@@ -303,8 +366,8 @@
         Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mBoundedPinnedStackBounds,
                 mPinnedStackBounds, velocityX, velocityY);
         if (!mPinnedStackBounds.equals(toBounds)) {
-            mPinnedStackBoundsAnimator = createResizePinnedStackAnimation(
-                toBounds, 0, FAST_OUT_SLOW_IN);
+            mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds,
+                toBounds, 0, FAST_OUT_SLOW_IN, mUpdatePinnedStackBoundsListener);
             mFlingAnimationUtils.apply(mPinnedStackBoundsAnimator, 0,
                 distanceBetweenRectOffsets(mPinnedStackBounds, toBounds),
                 velocity);
@@ -319,8 +382,8 @@
         Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mBoundedPinnedStackBounds,
                 mPinnedStackBounds);
         if (!mPinnedStackBounds.equals(toBounds)) {
-            mPinnedStackBoundsAnimator = createResizePinnedStackAnimation(
-                toBounds, SNAP_STACK_DURATION, FAST_OUT_SLOW_IN);
+            mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds,
+                toBounds, SNAP_STACK_DURATION, FAST_OUT_SLOW_IN, mUpdatePinnedStackBoundsListener);
             mPinnedStackBoundsAnimator.start();
         }
     }
@@ -335,8 +398,8 @@
         Rect toBounds = new Rect(mPinnedStackBounds);
         toBounds.offsetTo((int) offsetX, toBounds.top);
         if (!mPinnedStackBounds.equals(toBounds)) {
-            mPinnedStackBoundsAnimator = createResizePinnedStackAnimation(
-                toBounds, 0, FAST_OUT_SLOW_IN);
+            mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds,
+                toBounds, 0, FAST_OUT_SLOW_IN, mUpdatePinnedStackBoundsListener);
             mFlingAnimationUtils.apply(mPinnedStackBoundsAnimator, 0,
                 distanceBetweenRectOffsets(mPinnedStackBounds, toBounds),
                 velocityX);
@@ -364,8 +427,8 @@
             dismissBounds.centerY(),
             dismissBounds.centerX() + 1,
             dismissBounds.centerY() + 1);
-        mPinnedStackBoundsAnimator = createResizePinnedStackAnimation(
-            toBounds, DISMISS_STACK_DURATION, FAST_OUT_LINEAR_IN);
+        mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds,
+            toBounds, DISMISS_STACK_DURATION, FAST_OUT_LINEAR_IN, mUpdatePinnedStackBoundsListener);
         mPinnedStackBoundsAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -404,9 +467,9 @@
             StackInfo info = mActivityManager.getStackInfo(PINNED_STACK_ID);
             if (info != null) {
                 mPinnedStackBounds.set(info.bounds);
-                mBoundedPinnedStackBounds.set(mActivityManager.getPictureInPictureMovementBounds(
+                mBoundedPinnedStackBounds.set(mWindowManager.getPictureInPictureMovementBounds(
                         info.displayId));
-                mSnapAlgorithm = new PipSnapAlgorithm(mContext, info.displayId);
+                mSnapAlgorithm = new PipSnapAlgorithm(mContext);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Could not fetch PIP movement bounds.", e);
@@ -414,59 +477,6 @@
     }
 
     /**
-     * Moves the pinned stack to the given {@param left} and {@param top} offsets.
-     */
-    private void movePinnedStack(float left, float top) {
-        if ((int) left != mPinnedStackBounds.left || (int) top != mPinnedStackBounds.top) {
-            mPinnedStackBounds.offsetTo((int) left, (int) top);
-            BackgroundThread.getHandler().post(() -> {
-                try {
-                    mActivityManager.resizePinnedStack(mPinnedStackBounds,
-                            null /* tempPinnedBounds */);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Could not move pinned stack to offset: (" + left + ", " + top + ")",
-                            e);
-                }
-            });
-        }
-    }
-
-    /**
-     * Resizes the pinned stack to the given {@param bounds}.
-     */
-    private void resizePinnedStack(Rect bounds) {
-        if (!mPinnedStackBounds.equals(bounds)) {
-            mPinnedStackBounds.set(bounds);
-            BackgroundThread.getHandler().post(() -> {
-                try {
-                    mActivityManager.resizePinnedStack(bounds, null);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Could not resize pinned stack to bounds: (" + bounds + ")");
-                }
-            });
-        }
-    }
-
-    /**
-     * Creates a resize-stack animation.
-     */
-    private ValueAnimator createResizePinnedStackAnimation(Rect toBounds, int duration,
-            Interpolator interpolator) {
-        ValueAnimator anim = ValueAnimator.ofObject(RECT_EVALUATOR,
-                mPinnedStackBounds, toBounds);
-        anim.setDuration(duration);
-        anim.setInterpolator(interpolator);
-        anim.addUpdateListener(
-                new ValueAnimator.AnimatorUpdateListener() {
-                    @Override
-                    public void onAnimationUpdate(ValueAnimator animation) {
-                        resizePinnedStack((Rect) animation.getAnimatedValue());
-                    }
-                });
-        return anim;
-    }
-
-    /**
      * @return the distance between points {@param p1} and {@param p2}.
      */
     private float distanceBetweenRectOffsets(Rect r1, Rect r2) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 894bc53..5b93aa2 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -34,11 +34,14 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 import android.view.Display;
 import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -55,7 +58,7 @@
  */
 public class PipManager {
     private static final String TAG = "PipManager";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final boolean DEBUG_FORCE_ONBOARDING =
             SystemProperties.getBoolean("debug.tv.pip_force_onboarding", false);
 
@@ -135,6 +138,7 @@
     private Context mContext;
     private PipRecentsOverlayManager mPipRecentsOverlayManager;
     private IActivityManager mActivityManager;
+    private IWindowManager mWindowManager;
     private MediaSessionManager mMediaSessionManager;
     private int mState = STATE_NO_PIP;
     private final Handler mHandler = new Handler();
@@ -205,6 +209,7 @@
         mContext = context;
 
         mActivityManager = ActivityManagerNative.getDefault();
+        mWindowManager = WindowManagerGlobal.getWindowManagerService();
         SystemServicesProxy.getInstance(context).registerTaskStackListener(mTaskStackListener);
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
@@ -221,7 +226,7 @@
     private void loadConfigurationsAndApply() {
         Resources res = mContext.getResources();
         try {
-            mDefaultPipBounds = mActivityManager.getDefaultPictureInPictureBounds(
+            mDefaultPipBounds = mWindowManager.getPictureInPictureDefaultBounds(
                     Display.DEFAULT_DISPLAY);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to get default PIP bounds", e);
@@ -584,6 +589,10 @@
     private TaskStackListener mTaskStackListener = new TaskStackListener() {
         @Override
         public void onTaskStackChanged() {
+            if (DEBUG) Log.d(TAG, "onTaskStackChanged()");
+            if (!checkCurrentUserId()) {
+                return;
+            }
             if (mState != STATE_NO_PIP) {
                 boolean hasPip = false;
 
@@ -618,6 +627,9 @@
         @Override
         public void onActivityPinned() {
             if (DEBUG) Log.d(TAG, "onActivityPinned()");
+            if (!checkCurrentUserId()) {
+                return;
+            }
             StackInfo stackInfo = getPinnedStackInfo();
             if (stackInfo == null) {
                 Log.w(TAG, "Cannot find pinned stack");
@@ -648,6 +660,9 @@
         @Override
         public void onPinnedActivityRestartAttempt() {
             if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
+            if (!checkCurrentUserId()) {
+                return;
+            }
             // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
             movePipToFullscreen();
         }
@@ -655,6 +670,9 @@
         @Override
         public void onPinnedStackAnimationEnded() {
             if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
+            if (!checkCurrentUserId()) {
+                return;
+            }
             switch (mState) {
                 case STATE_PIP_OVERLAY:
                     if (!mPipRecentsOverlayManager.isRecentsShown()) {
@@ -675,6 +693,26 @@
                     break;
             }
         }
+
+        // {@link android.app.ITaskStackListener} isn't multi-user aware.
+        // Check the current uid and current SystemUI's running uid
+        // so we can handle the PIP status change only once.
+        private boolean checkCurrentUserId() {
+            try {
+                int processUserId = UserHandle.myUserId();
+                int currentUserId = mActivityManager.getCurrentUser().id;
+                if (processUserId != currentUserId) {
+                    if (DEBUG) {
+                        Log.d(TAG, "UID mismatch. SystemUI is running uid=" + processUserId
+                            + " and the current user is uid=" + currentUserId);
+                    }
+                    return false;
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "Unable to get current user.");
+            }
+            return true;
+        }
     };
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 6a1b477..c91b060 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -160,7 +160,7 @@
      * ActivityManagerNative.
      * This simply passes callbacks to listeners through {@link H}.
      * */
-    private ITaskStackListener.Stub mTaskStackListener = new ITaskStackListener.Stub() {
+    private android.app.TaskStackListener mTaskStackListener = new android.app.TaskStackListener() {
         @Override
         public void onTaskStackChanged() throws RemoteException {
             mHandler.removeMessages(H.ON_TASK_STACK_CHANGED);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 9e9bdd7..0e074c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -17,20 +17,17 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.os.Build;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
-import android.util.Log;
 import android.util.MathUtils;
 import android.util.SparseBooleanArray;
 
-import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.systemui.R;
 
 import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 public class DozeParameters {
     private static final int MAX_DURATION = 60 * 1000;
@@ -115,6 +112,12 @@
         return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold);
     }
 
+    public boolean getAlwaysOn() {
+        return Build.IS_DEBUGGABLE
+                && Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                Settings.Secure.DOZE_ALWAYS_ON, 0, UserHandle.USER_CURRENT) != 0;
+    }
+
     private boolean getBoolean(String propName, int resId) {
         return SystemProperties.getBoolean(propName, mContext.getResources().getBoolean(resId));
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index b44f5f7..b141454 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.os.Handler;
 import android.util.Log;
+import android.view.View;
 import android.view.animation.Interpolator;
 
 import com.android.systemui.Interpolators;
@@ -40,6 +41,8 @@
     private final Handler mHandler = new Handler();
     private final ScrimController mScrimController;
 
+    private final View mStackScroller;
+
     private boolean mDozing;
     private DozeHost.PulseCallback mPulseCallback;
     private int mPulseReason;
@@ -48,7 +51,9 @@
     private float mInFrontTarget;
     private float mBehindTarget;
 
-    public DozeScrimController(ScrimController scrimController, Context context) {
+    public DozeScrimController(ScrimController scrimController, Context context,
+            View stackScroller) {
+        mStackScroller = stackScroller;
         mScrimController = scrimController;
         mDozeParameters = new DozeParameters(context);
     }
@@ -59,7 +64,11 @@
         if (mDozing) {
             abortAnimations();
             mScrimController.setDozeBehindAlpha(1f);
-            mScrimController.setDozeInFrontAlpha(1f);
+            mScrimController.setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? 0f : 1f);
+            if (mDozeParameters.getAlwaysOn()) {
+                mStackScroller.setAlpha(0f);
+                mHandler.postDelayed(() -> mStackScroller.setAlpha(0f), 30);
+            }
         } else {
             cancelPulsing();
             if (animate) {
@@ -74,6 +83,9 @@
                 mScrimController.setDozeBehindAlpha(0f);
                 mScrimController.setDozeInFrontAlpha(0f);
             }
+            if (mDozeParameters.getAlwaysOn()) {
+                mStackScroller.setAlpha(1f);
+            }
         }
     }
 
@@ -93,7 +105,9 @@
         // be invoked when we're done so that the caller can drop the pulse wakelock.
         mPulseCallback = callback;
         mPulseReason = reason;
-        mHandler.post(mPulseIn);
+        if (mDozeParameters.getAlwaysOn()) {
+            mHandler.post(mPulseIn);
+        }
     }
 
     /**
@@ -111,6 +125,9 @@
         if (isPulsing()) {
             final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP
                     || mPulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
+            if (mDozeParameters.getAlwaysOn()) {
+                mStackScroller.setAlpha(1f);
+            }
             startScrimAnimation(true /* inFront */, 0f,
                     mDozeParameters.getPulseInDuration(pickupOrDoubleTap),
                     pickupOrDoubleTap ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT,
@@ -245,6 +262,10 @@
 
             // Signal that the pulse is ready to turn the screen on and draw.
             pulseStarted();
+
+            if (mDozeParameters.getAlwaysOn()) {
+                mHandler.post(DozeScrimController.this::onScreenTurnedOn);
+            }
         }
     };
 
@@ -262,7 +283,8 @@
         public void run() {
             if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
             if (!mDozing) return;
-            startScrimAnimation(true /* inFront */, 1f, mDozeParameters.getPulseOutDuration(),
+            startScrimAnimation(true /* inFront */, mDozeParameters.getAlwaysOn() ? 0 : 1,
+                    mDozeParameters.getPulseOutDuration(),
                     Interpolators.ALPHA_IN, mPulseOutFinished);
         }
     };
@@ -271,6 +293,9 @@
         @Override
         public void run() {
             if (DEBUG) Log.d(TAG, "Pulse out finished");
+            if (mDozeParameters.getAlwaysOn()) {
+                mStackScroller.setAlpha(0f);
+            }
             DozeLog.tracePulseFinish();
 
             // Signal that the pulse is all finished so we can turn the screen off now.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 28e5f7e..0ef2687 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -123,6 +123,7 @@
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.keyguard.KeyguardHostView.OnDismissAction;
+import com.android.keyguard.KeyguardStatusView;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.ViewMediatorCallback;
@@ -367,7 +368,7 @@
     // top bar
     BaseStatusBarHeader mHeader;
     protected KeyguardStatusBarView mKeyguardStatusBar;
-    View mKeyguardStatusView;
+    KeyguardStatusView mKeyguardStatusView;
     KeyguardBottomAreaView mKeyguardBottomArea;
     boolean mLeaveOpenOnKeyguardHide;
     KeyguardIndicationController mKeyguardIndicationController;
@@ -846,10 +847,11 @@
         mHeadsUpManager.addListener(mScrimController);
         mStackScroller.setScrimController(mScrimController);
         mStatusBarView.setScrimController(mScrimController);
-        mDozeScrimController = new DozeScrimController(mScrimController, context);
+        mDozeScrimController = new DozeScrimController(mScrimController, context, mStackScroller);
 
         mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
-        mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
+        mKeyguardStatusView =
+                (KeyguardStatusView) mStatusBarWindow.findViewById(R.id.keyguard_status_view);
         mKeyguardBottomArea =
                 (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
         mKeyguardBottomArea.setActivityStarter(this);
@@ -2695,6 +2697,10 @@
         mFalsingManager.onScreenOff();
     }
 
+    public boolean isPulsing() {
+        return mDozeScrimController.isPulsing();
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
@@ -5135,6 +5141,11 @@
         }
 
         @Override
+        public void dozeTimeTick() {
+            mKeyguardStatusView.refreshTime();
+        }
+
+        @Override
         public boolean isPowerSaveActive() {
             return mBatteryController != null && mBatteryController.isPowerSave();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 4425c5e..5696123 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -248,6 +248,10 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (mService.isDozing() && !mService.isPulsing()) {
+            // Discard all touch events in always-on.
+            return true;
+        }
         boolean intercept = false;
         if (mNotificationPanel.isFullyExpanded()
                 && mStackScrollLayout.getVisibility() == View.VISIBLE
@@ -274,6 +278,11 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
+        if (mService.isDozing() && !mService.isPulsing()) {
+            // Discard all touch events in always-on.
+            return true;
+        }
+
         boolean handled = false;
         if (mService.getBarState() == StatusBarState.KEYGUARD) {
             handled = mDragDownHelper.onTouchEvent(ev);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f9121c3..40f0aae 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -76,6 +76,7 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackId;
 import android.app.ActivityManager.StackInfo;
+import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityManager.TaskThumbnailInfo;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerInternal.SleepToken;
@@ -524,9 +525,6 @@
     static final int ALLOW_NON_FULL_IN_PROFILE = 1;
     static final int ALLOW_FULL_ONLY = 2;
 
-    // Delay in notifying task stack change listeners (in millis)
-    static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
-
     // Necessary ApplicationInfo flags to mark an app as persistent
     private static final int PERSISTENT_MASK =
             ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
@@ -554,9 +552,7 @@
 
     final ActivityStarter mActivityStarter;
 
-    /** Task stack change listeners. */
-    private final RemoteCallbackList<ITaskStackListener> mTaskStackListeners =
-            new RemoteCallbackList<ITaskStackListener>();
+    final TaskChangeNotificationController mTaskChangeNotificationController;
 
     final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter();
 
@@ -1396,12 +1392,6 @@
 
     final long[] mTmpLong = new long[2];
 
-    // The size and position information that describes where the pinned stack will go by default.
-    // In particular, the size is defined in DPs.
-    Size mDefaultPinnedStackSizeDp;
-    Size mDefaultPinnedStackScreenEdgeInsetsDp;
-    int mDefaultPinnedStackGravity;
-
     static final class ProcessChangeItem {
         static final int CHANGE_ACTIVITIES = 1<<0;
         static final int CHANGE_PROCESS_STATE = 1<<1;
@@ -1526,29 +1516,23 @@
     static final int START_USER_SWITCH_UI_MSG = 46;
     static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
     static final int DISMISS_DIALOG_UI_MSG = 48;
-    static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 49;
-    static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 50;
-    static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 51;
-    static final int DELETE_DUMPHEAP_MSG = 52;
-    static final int FOREGROUND_PROFILE_CHANGED_MSG = 53;
-    static final int DISPATCH_UIDS_CHANGED_UI_MSG = 54;
-    static final int REPORT_TIME_TRACKER_MSG = 55;
-    static final int REPORT_USER_SWITCH_COMPLETE_MSG = 56;
-    static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 57;
-    static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 59;
-    static final int IDLE_UIDS_MSG = 60;
-    static final int SYSTEM_USER_UNLOCK_MSG = 61;
-    static final int LOG_STACK_STATE = 62;
-    static final int VR_MODE_CHANGE_MSG = 63;
-    static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 64;
-    static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 65;
-    static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 66;
-    static final int NOTIFY_FORCED_RESIZABLE_MSG = 67;
-    static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 68;
-    static final int VR_MODE_APPLY_IF_NEEDED_MSG = 69;
-    static final int SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG = 70;
-    static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 71;
-    static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 72;
+    static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 49;
+    static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 50;
+    static final int DELETE_DUMPHEAP_MSG = 51;
+    static final int FOREGROUND_PROFILE_CHANGED_MSG = 52;
+    static final int DISPATCH_UIDS_CHANGED_UI_MSG = 53;
+    static final int REPORT_TIME_TRACKER_MSG = 54;
+    static final int REPORT_USER_SWITCH_COMPLETE_MSG = 55;
+    static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 56;
+    static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 57;
+    static final int IDLE_UIDS_MSG = 58;
+    static final int SYSTEM_USER_UNLOCK_MSG = 59;
+    static final int LOG_STACK_STATE = 60;
+    static final int VR_MODE_CHANGE_MSG = 61;
+    static final int VR_MODE_APPLY_IF_NEEDED_MSG = 62;
+    static final int SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG = 63;
+    static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 64;
+    static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 65;
     static final int START_USER_SWITCH_FG_MSG = 712;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
@@ -2105,92 +2089,6 @@
                 }
                 break;
             }
-            case NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
-                        try {
-                            // Make a one-way callback to the listener
-                            mTaskStackListeners.getBroadcastItem(i).onTaskStackChanged();
-                        } catch (RemoteException e){
-                            // Handled by the RemoteCallbackList
-                        }
-                    }
-                    mTaskStackListeners.finishBroadcast();
-                }
-                break;
-            }
-            case NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
-                        try {
-                            // Make a one-way callback to the listener
-                            mTaskStackListeners.getBroadcastItem(i).onActivityPinned();
-                        } catch (RemoteException e){
-                            // Handled by the RemoteCallbackList
-                        }
-                    }
-                    mTaskStackListeners.finishBroadcast();
-                }
-                break;
-            }
-            case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
-                        try {
-                            // Make a one-way callback to the listener
-                            mTaskStackListeners.getBroadcastItem(i).onPinnedActivityRestartAttempt();
-                        } catch (RemoteException e){
-                            // Handled by the RemoteCallbackList
-                        }
-                    }
-                    mTaskStackListeners.finishBroadcast();
-                }
-                break;
-            }
-            case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
-                        try {
-                            // Make a one-way callback to the listener
-                            mTaskStackListeners.getBroadcastItem(i).onPinnedStackAnimationEnded();
-                        } catch (RemoteException e){
-                            // Handled by the RemoteCallbackList
-                        }
-                    }
-                    mTaskStackListeners.finishBroadcast();
-                }
-                break;
-            }
-            case NOTIFY_FORCED_RESIZABLE_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
-                        try {
-                            // Make a one-way callback to the listener
-                            mTaskStackListeners.getBroadcastItem(i).onActivityForcedResizable(
-                                    (String) msg.obj, msg.arg1);
-                        } catch (RemoteException e){
-                            // Handled by the RemoteCallbackList
-                        }
-                    }
-                    mTaskStackListeners.finishBroadcast();
-                }
-                break;
-            }
-                case NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG: {
-                    synchronized (ActivityManagerService.this) {
-                        for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
-                            try {
-                                // Make a one-way callback to the listener
-                                mTaskStackListeners.getBroadcastItem(i)
-                                        .onActivityDismissingDockedStack();
-                            } catch (RemoteException e){
-                                // Handled by the RemoteCallbackList
-                            }
-                        }
-                        mTaskStackListeners.finishBroadcast();
-                    }
-                    break;
-                }
             case NOTIFY_CLEARTEXT_NETWORK_MSG: {
                 final int uid = msg.arg1;
                 final byte[] firstPacket = (byte[]) msg.obj;
@@ -2322,11 +2220,6 @@
             case IDLE_UIDS_MSG: {
                 idleUids();
             } break;
-            case LOG_STACK_STATE: {
-                synchronized (ActivityManagerService.this) {
-                    mStackSupervisor.logStackState();
-                }
-            } break;
             case VR_MODE_CHANGE_MSG: {
                 VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
                 if (vrService == null) {
@@ -2730,6 +2623,8 @@
         mStackSupervisor.onConfigurationChanged(mTempConfig);
         mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
         mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
+        mTaskChangeNotificationController =
+                new TaskChangeNotificationController(this, mStackSupervisor, mHandler);
         mActivityStarter = new ActivityStarter(this, mStackSupervisor);
         mRecentTasks = new RecentTasks(this, mStackSupervisor);
 
@@ -3104,13 +2999,18 @@
     @Override
     public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException {
         enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "registerTaskStackListener()");
-        synchronized (this) {
-            if (listener != null) {
-                mTaskStackListeners.register(listener);
-            }
-        }
+        mTaskChangeNotificationController.registerTaskStackListener(listener);
     }
 
+    /**
+     * Unregister a task stack listener so that it stops receiving callbacks.
+     */
+    @Override
+    public void unregisterTaskStackListener(ITaskStackListener listener) throws RemoteException {
+         enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "unregisterTaskStackListener()");
+         mTaskChangeNotificationController.unregisterTaskStackListener(listener);
+     }
+
     @Override
     public void notifyActivityDrawn(IBinder token) {
         if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token);
@@ -7577,7 +7477,8 @@
                 // current bounds.
                 final ActivityStack pinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID);
                 final Rect bounds = (pinnedStack != null)
-                        ? pinnedStack.mBounds : getDefaultPictureInPictureBounds(DEFAULT_DISPLAY);
+                        ? pinnedStack.mBounds
+                        : mWindowManager.getPictureInPictureDefaultBounds(DEFAULT_DISPLAY);
 
                 mStackSupervisor.moveActivityToPinnedStackLocked(
                         r, "enterPictureInPictureMode", bounds);
@@ -7587,85 +7488,6 @@
         }
     }
 
-    @Override
-    public Rect getDefaultPictureInPictureBounds(int displayId) {
-        final long origId = Binder.clearCallingIdentity();
-        final Rect defaultBounds = new Rect();
-        try {
-            synchronized(this) {
-                if (!mSupportsPictureInPicture) {
-                    return new Rect();
-                }
-
-                // Convert the sizes to for the current display state
-                final DisplayMetrics dm = mStackSupervisor.getDisplayRealMetrics(displayId);
-                final int stackWidth = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
-                        mDefaultPinnedStackSizeDp.getWidth(), dm);
-                final int stackHeight = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
-                        mDefaultPinnedStackSizeDp.getHeight(), dm);
-                final Rect maxBounds = new Rect();
-                getPictureInPictureBounds(displayId, maxBounds);
-                Gravity.apply(mDefaultPinnedStackGravity, stackWidth, stackHeight,
-                        maxBounds, 0, 0, defaultBounds);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-        return defaultBounds;
-    }
-
-    @Override
-    public Rect getPictureInPictureMovementBounds(int displayId) {
-        final long origId = Binder.clearCallingIdentity();
-        final Rect maxBounds = new Rect();
-        try {
-            synchronized(this) {
-                if (!mSupportsPictureInPicture) {
-                    return new Rect();
-                }
-
-                getPictureInPictureBounds(displayId, maxBounds);
-
-                // Adjust the max bounds by the current stack dimensions
-                final StackInfo pinnedStackInfo = mStackSupervisor.getStackInfoLocked(
-                        PINNED_STACK_ID);
-                if (pinnedStackInfo != null) {
-                    maxBounds.right = Math.max(maxBounds.left, maxBounds.right -
-                            pinnedStackInfo.bounds.width());
-                    maxBounds.bottom = Math.max(maxBounds.top, maxBounds.bottom -
-                            pinnedStackInfo.bounds.height());
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-        return maxBounds;
-    }
-
-    /**
-     * Calculate the bounds where the pinned stack can move in the current display state.
-     */
-    private void getPictureInPictureBounds(int displayId, Rect outRect) {
-        // Convert the insets to for the current display state
-        final DisplayMetrics dm = mStackSupervisor.getDisplayRealMetrics(displayId);
-        final int insetsLR = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
-                mDefaultPinnedStackScreenEdgeInsetsDp.getWidth(), dm);
-        final int insetsTB = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
-                mDefaultPinnedStackScreenEdgeInsetsDp.getHeight(), dm);
-        try {
-            final Point displaySize = mStackSupervisor.getDisplayRealSize(displayId);
-            final Rect insets = new Rect();
-            mWindowManager.getStableInsets(displayId, insets);
-
-            // Calculate the insets from the system decorations and apply the gravity
-            outRect.set(insets.left + insetsLR, insets.top + insetsTB,
-                    displaySize.x - insets.right - insetsLR,
-                    displaySize.y - insets.bottom - insetsTB);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to calculate PIP movement bounds", e);
-        }
-    }
-
     // =========================================================
     // PROCESS INFO
     // =========================================================
@@ -7753,7 +7575,8 @@
             try {
                 PermissionInfo info = mActivityManagerService.mContext.getPackageManager()
                         .getPermissionInfo(permission, 0);
-                return info.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS;
+                return (info.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+                        == PermissionInfo.PROTECTION_DANGEROUS;
             } catch (NameNotFoundException nnfe) {
                 Slog.e(TAG, "No such permission: "+ permission, nnfe);
             }
@@ -9411,7 +9234,6 @@
 
                 task.setLastThumbnailLocked(thumbnail);
                 task.freeLastThumbnail();
-
                 return task.taskId;
             }
         } finally {
@@ -9433,6 +9255,7 @@
             if (r != null) {
                 r.setTaskDescription(td);
                 r.task.updateTaskDescription();
+                mTaskChangeNotificationController.notifyTaskDescriptionChanged(r.task.taskId, td);
             }
         }
     }
@@ -11714,38 +11537,10 @@
         mRecentTasks.notifyTaskPersisterLocked(task, flush);
     }
 
-    /** Notifies all listeners when the task stack has changed. */
-    void notifyTaskStackChangedLocked() {
-        mHandler.sendEmptyMessage(LOG_STACK_STATE);
-        mHandler.removeMessages(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
-        Message nmsg = mHandler.obtainMessage(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
-        mHandler.sendMessageDelayed(nmsg, NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY);
-    }
-
-    /** Notifies all listeners when an Activity is pinned. */
-    void notifyActivityPinnedLocked() {
-        mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
-        mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG).sendToTarget();
-    }
-
-    /**
-     * Notifies all listeners when an attempt was made to start an an activity that is already
-     * running in the pinned stack and the activity was not actually started, but the task is
-     * either brought to the front or a new Intent is delivered to it.
-     */
-    void notifyPinnedActivityRestartAttemptLocked() {
-        mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
-        mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG).sendToTarget();
-    }
-
     /** Notifies all listeners when the pinned stack animation ends. */
     @Override
     public void notifyPinnedStackAnimationEnded() {
-        synchronized (this) {
-            mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
-            mHandler.obtainMessage(
-                    NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG).sendToTarget();
-        }
+        mTaskChangeNotificationController.notifyPinnedStackAnimationEnded();
     }
 
     @Override
@@ -13204,7 +12999,6 @@
             mLenientBackgroundCheck = lenientBackgroundCheck;
             mSupportsLeanbackOnly = supportsLeanbackOnly;
             mForceResizableActivities = forceResizable;
-            mWindowManager.setForceResizableTasks(mForceResizableActivities);
             if (supportsMultiWindow || forceResizable) {
                 mSupportsMultiWindow = true;
                 mSupportsFreeformWindowManagement = freeformWindowManagement || forceResizable;
@@ -13214,6 +13008,8 @@
                 mSupportsFreeformWindowManagement = false;
                 mSupportsPictureInPicture = false;
             }
+            mWindowManager.setForceResizableTasks(mForceResizableActivities);
+            mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
             // This happens before any activities are started, so we can change global configuration
             // in-place.
             updateConfigurationLocked(configuration, null, true);
@@ -13227,12 +13023,6 @@
                     com.android.internal.R.dimen.thumbnail_width);
             mThumbnailHeight = res.getDimensionPixelSize(
                     com.android.internal.R.dimen.thumbnail_height);
-            mDefaultPinnedStackSizeDp = Size.parseSize(res.getString(
-                    com.android.internal.R.string.config_defaultPictureInPictureSize));
-            mDefaultPinnedStackScreenEdgeInsetsDp = Size.parseSize(res.getString(
-                    com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets));
-            mDefaultPinnedStackGravity = res.getInteger(
-                    com.android.internal.R.integer.config_defaultPictureInPictureGravity);
             mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
                     com.android.internal.R.string.config_appsNotReportingCrashes));
             mUserController.mUserSwitchUiEnabled = !res.getBoolean(
@@ -14314,13 +14104,6 @@
                 }
             } else if ("locks".equals(cmd)) {
                 LockGuard.dump(fd, pw, args);
-            } else if ("pip".equals(cmd)) {
-                Rect bounds = getDefaultPictureInPictureBounds(DEFAULT_DISPLAY);
-                pw.print("defaultBounds="); bounds.printShortString(pw);
-                pw.println();
-                bounds = getPictureInPictureMovementBounds(DEFAULT_DISPLAY);
-                pw.print("movementBounds="); bounds.printShortString(pw);
-                pw.println();
             } else {
                 // Dumping a single activity?
                 if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll, dumpVisibleStacks)) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 7a692b6..1484420 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2498,7 +2498,6 @@
             pw.println("    p[rocesses] [PACKAGE_NAME]: process state");
             pw.println("    o[om]: out of memory management");
             pw.println("    perm[issions]: URI permission grant state");
-            pw.println("    pip: PIP state");
             pw.println("    prov[iders] [COMP_SPEC ...]: content provider state");
             pw.println("    provider [COMP_SPEC]: provider client-side state");
             pw.println("    s[ervices] [COMP_SPEC ...]: service state");
@@ -2702,8 +2701,6 @@
             pw.println("           Test command for sizing <TASK_ID> by <STEP_SIZE>");
             pw.println("           increments within the screen applying the optional [DELAY_MS] between");
             pw.println("           each step.");
-            pw.println("  pip");
-            pw.println("      Gets the current PIP state.");
             pw.println("  write");
             pw.println("      Write all pending state to storage.");
             pw.println();
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index abe4f30..df03af2 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -664,10 +664,18 @@
         hasBeenLaunched = false;
         mStackSupervisor = supervisor;
         mInitialActivityContainer = container;
+
+        mRotationAnimationHint = aInfo.rotationAnimation;
+
         if (options != null) {
             pendingOptions = options;
             mLaunchTaskBehind = pendingOptions.getLaunchTaskBehind();
-            mRotationAnimationHint = pendingOptions.getRotationAnimationHint();
+
+            final int rotationAnimation = pendingOptions.getRotationAnimationHint();
+            // Only override manifest supplied option if set.
+            if (rotationAnimation >= 0) {
+                mRotationAnimationHint = rotationAnimation;
+            }
             PendingIntent usageReport = pendingOptions.getUsageTimeReport();
             if (usageReport != null) {
                 appTimeTracker = new AppTimeTracker(usageReport);
@@ -932,6 +940,10 @@
             if (stopped) {
                 clearOptionsLocked();
             }
+
+            if (service != null) {
+                service.mTaskChangeNotificationController.notifyTaskStackChanged();
+            }
         }
     }
 
@@ -1733,6 +1745,8 @@
                 mStackSupervisor.resumeFocusedStackTopActivityLocked();
             }
         }
+        service.mTaskChangeNotificationController.notifyActivityRequestedOrientationChanged(
+                task.taskId, requestedOrientation);
     }
 
     // TODO: now used only in one place to address race-condition. Remove when that will be fixed.
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 3dbbb13..fc41fd3 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1279,7 +1279,7 @@
         // task stack changes, because its positioning may depend on it.
         if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
                 || mService.mStackSupervisor.getStack(PINNED_STACK_ID) != null) {
-            mService.notifyTaskStackChangedLocked();
+            mService.mTaskChangeNotificationController.notifyTaskStackChanged();
             mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
         }
 
@@ -3314,9 +3314,11 @@
         final boolean endTask = index <= 0;
         final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
         if (mResumedActivity == r) {
-
             if (DEBUG_VISIBILITY || DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                     "Prepare close transition: finishing " + r);
+            if (endTask) {
+                mService.mTaskChangeNotificationController.notifyTaskRemovalStarted(task.taskId);
+            }
             mWindowManager.prepareAppTransition(transit, false);
 
             // Tell window manager to prepare for this one to be removed.
@@ -4138,6 +4140,8 @@
 
         mStackSupervisor.resumeFocusedStackTopActivityLocked();
         EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
+
+        mService.mTaskChangeNotificationController.notifyTaskMovedToFront(tr.taskId);
     }
 
     /**
@@ -4690,6 +4694,7 @@
                 // default configuration the next time it launches.
                 task.updateOverrideConfiguration(null);
             }
+            mService.mTaskChangeNotificationController.notifyTaskRemoved(task.taskId);
         }
 
         final ActivityRecord r = mResumedActivity;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 99c5b06..f0427e4 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -157,8 +157,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.ANIMATE;
 import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
-import static com.android.server.am.ActivityManagerService.NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG;
-import static com.android.server.am.ActivityManagerService.NOTIFY_FORCED_RESIZABLE_MSG;
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
@@ -2582,7 +2580,7 @@
         resumeFocusedStackTopActivityLocked();
 
         mWindowManager.animateResizePinnedStack(bounds, -1);
-        mService.notifyActivityPinnedLocked();
+        mService.mTaskChangeNotificationController.notifyActivityPinned();
     }
 
     boolean moveFocusableActivityStackToFrontLocked(ActivityRecord r, String reason) {
@@ -2904,7 +2902,7 @@
         r.mLaunchTaskBehind = false;
         task.setLastThumbnailLocked(r.screenshotActivityLocked());
         mRecentTasks.addLocked(task);
-        mService.notifyTaskStackChangedLocked();
+        mService.mTaskChangeNotificationController.notifyTaskStackChanged();
         mWindowManager.setAppVisibility(r.appToken, false);
 
         // When launching tasks behind, update the last active time of the top task after the new
@@ -3422,18 +3420,6 @@
         mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_CHANGED, displayId, 0));
     }
 
-    DisplayMetrics getDisplayRealMetrics(int displayId) {
-        final ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
-        activityDisplay.mDisplay.getRealMetrics(activityDisplay.mRealMetrics);
-        return activityDisplay.mRealMetrics;
-    }
-
-    Point getDisplayRealSize(int displayId) {
-        final ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
-        activityDisplay.mDisplay.getRealSize(activityDisplay.mRealSize);
-        return activityDisplay.mRealSize;
-    }
-
     private void handleDisplayAdded(int displayId) {
         boolean newDisplay;
         synchronized (mService) {
@@ -3588,7 +3574,7 @@
         final ActivityRecord topActivity = task.getTopActivity();
         if (!task.canGoInDockedStack() || forceNonResizable) {
             // Display a warning toast that we tried to put a non-dockable task in the docked stack.
-            mService.mHandler.sendEmptyMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
+            mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
 
             // Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
             // we need to move it to top of fullscreen stack, otherwise it will be covered.
@@ -3596,8 +3582,8 @@
         } else if (topActivity != null && topActivity.isNonResizableOrForced()
                 && !topActivity.noDisplay) {
             String packageName = topActivity.appInfo.packageName;
-            mService.mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, task.taskId, 0,
-                    packageName).sendToTarget();
+            mService.mTaskChangeNotificationController.notifyActivityForcedResizable(
+                    task.taskId, packageName);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 20a14d3..006c13d 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -631,7 +631,7 @@
             // The activity was already running in the pinned stack so it wasn't started, but either
             // brought to the front or the new intent was delivered to it since it was already in
             // front. Notify anyone interested in this piece of information.
-            mService.notifyPinnedActivityRestartAttemptLocked();
+            mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt();
             return;
         }
     }
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
new file mode 100644
index 0000000..b6e35d2
--- /dev/null
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.app.ITaskStackListener;
+import android.app.ActivityManager.TaskDescription;
+import android.content.ComponentName;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+
+class TaskChangeNotificationController {
+    static final int LOG_STACK_STATE_MSG = 1;
+    static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2;
+    static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3;
+    static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4;
+    static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 5;
+    static final int NOTIFY_FORCED_RESIZABLE_MSG = 6;
+    static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7;
+    static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8;
+    static final int NOTIFY_TASK_REMOVED_LISTENERS_MSG = 9;
+    static final int NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG = 10;
+    static final int NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG = 11;
+    static final int NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS = 12;
+    static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13;
+
+    // Delay in notifying task stack change listeners (in millis)
+    static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
+
+    private final ActivityManagerService mService;
+    private final ActivityStackSupervisor mStackSupervisor;
+    private final Handler mHandler;
+
+    /** Task stack change listeners. */
+    private final RemoteCallbackList<ITaskStackListener> mTaskStackListeners =
+            new RemoteCallbackList<ITaskStackListener>();
+
+    @FunctionalInterface
+    public interface ConsumerWithRemoteException<T> {
+        void accept(T t) throws RemoteException;
+    }
+
+    private class MainHandler extends Handler {
+        public MainHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case LOG_STACK_STATE_MSG: {
+                    synchronized (mService) {
+                        mStackSupervisor.logStackState();
+                    }
+                    break;
+                }
+                case NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG:
+                    forAllListeners((listener) -> listener.onTaskStackChanged());
+                    break;
+                case NOTIFY_TASK_ADDED_LISTENERS_MSG:
+                    forAllListeners((listener) -> listener.onTaskCreated(msg.arg1,
+                            (ComponentName) msg.obj));
+                    break;
+                case NOTIFY_TASK_REMOVED_LISTENERS_MSG:
+                    forAllListeners((listener) -> listener.onTaskRemoved(msg.arg1));
+                    break;
+                case NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG:
+                    forAllListeners((listener) -> listener.onTaskMovedToFront(msg.arg1));
+                    break;
+                case NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG:
+                    forAllListeners((listener) -> listener.onTaskDescriptionChanged(msg.arg1,
+                            (TaskDescription) msg.obj));
+                    break;
+                case NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS:
+                    forAllListeners((listener) -> listener.onActivityRequestedOrientationChanged(
+                            msg.arg1, msg.arg2));
+                case NOTIFY_TASK_REMOVAL_STARTED_LISTENERS:
+                    forAllListeners((listener) -> listener.onTaskRemovalStarted(msg.arg1));
+                    break;
+                case NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG:
+                    forAllListeners((listener) -> listener.onActivityPinned());
+                    break;
+                case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG:
+                    forAllListeners((listener) -> listener.onPinnedActivityRestartAttempt());
+                    break;
+                case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG:
+                    forAllListeners((listener) -> listener.onPinnedStackAnimationEnded());
+                    break;
+                case NOTIFY_FORCED_RESIZABLE_MSG:
+                    forAllListeners((listener) -> listener.onActivityForcedResizable(
+                            (String) msg.obj, msg.arg1));
+                    break;
+                case NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG:
+                    forAllListeners((listener) -> listener.onActivityDismissingDockedStack());
+                    break;
+            }
+        }
+    }
+
+    public TaskChangeNotificationController(ActivityManagerService service,
+            ActivityStackSupervisor stackSupervisor, Handler handler) {
+        mService = service;
+        mStackSupervisor = stackSupervisor;
+        mHandler = new MainHandler(handler.getLooper());
+    }
+
+    public void registerTaskStackListener(ITaskStackListener listener) {
+        synchronized (mService) {
+            if (listener != null) {
+                mTaskStackListeners.register(listener);
+            }
+        }
+    }
+
+    public void unregisterTaskStackListener(ITaskStackListener listener) {
+        synchronized (mService) {
+            if (listener != null) {
+                mTaskStackListeners.unregister(listener);
+            }
+        }
+    }
+
+    void forAllListeners(ConsumerWithRemoteException<ITaskStackListener> callback) {
+        synchronized (mService) {
+            for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
+                try {
+                    // Make a one-way callback to the listener
+                    callback.accept(mTaskStackListeners.getBroadcastItem(i));
+                } catch (RemoteException e) {
+                    // Handled by the RemoteCallbackList.
+                }
+            }
+            mTaskStackListeners.finishBroadcast();
+         }
+      }
+
+    /** Notifies all listeners when the task stack has changed. */
+    void notifyTaskStackChanged() {
+        mHandler.sendEmptyMessage(LOG_STACK_STATE_MSG);
+        mHandler.removeMessages(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
+        Message msg = mHandler.obtainMessage(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
+        // Only the main task stack change notification requires a delay.
+        mHandler.sendMessageDelayed(msg, NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY);
+    }
+
+    /** Notifies all listeners when an Activity is pinned. */
+    void notifyActivityPinned() {
+        mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
+        mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG).sendToTarget();
+    }
+
+    /**
+     * Notifies all listeners when an attempt was made to start an an activity that is already
+     * running in the pinned stack and the activity was not actually started, but the task is
+     * either brought to the front or a new Intent is delivered to it.
+     */
+    void notifyPinnedActivityRestartAttempt() {
+        mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
+        mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG).sendToTarget();
+    }
+
+    /** Notifies all listeners when the pinned stack animation ends. */
+    void notifyPinnedStackAnimationEnded() {
+        mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
+        mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG)
+                .sendToTarget();
+    }
+
+    void notifyActivityDismissingDockedStack() {
+        mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
+        mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG).sendToTarget();
+    }
+
+    void notifyActivityForcedResizable(int taskId, String packageName) {
+        mHandler.removeMessages(NOTIFY_FORCED_RESIZABLE_MSG);
+        mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, taskId, 0 /* unused */, packageName)
+                .sendToTarget();
+    }
+
+    void notifyTaskCreated(int taskId, ComponentName componentName) {
+        mHandler.obtainMessage(NOTIFY_TASK_ADDED_LISTENERS_MSG, taskId, 0 /* unused */,
+                componentName).sendToTarget();
+    }
+
+    void notifyTaskRemoved(int taskId) {
+        mHandler.obtainMessage(NOTIFY_TASK_REMOVED_LISTENERS_MSG, taskId, 0 /* unused */)
+                .sendToTarget();
+    }
+
+    void notifyTaskMovedToFront(int taskId) {
+        mHandler.obtainMessage(NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG, taskId, 0 /* unused */)
+                .sendToTarget();
+    }
+
+    void notifyTaskDescriptionChanged(int taskId, TaskDescription taskDescription) {
+        mHandler.obtainMessage(NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG, taskId,
+                0 /* unused */, taskDescription).sendToTarget();
+    }
+
+    void notifyActivityRequestedOrientationChanged(int taskId, int orientation) {
+        mHandler.obtainMessage(NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS, taskId,
+                orientation).sendToTarget();
+    }
+
+    /**
+     * Notify listeners that the task is about to be finished before its surfaces are removed from
+     * the window manager. This allows interested parties to perform relevant animations before
+     * the window disappears.
+     */
+    void notifyTaskRemovalStarted(int taskId) {
+        mHandler.obtainMessage(NOTIFY_TASK_REMOVAL_STARTED_LISTENERS, taskId, 0 /* unused */)
+                .sendToTarget();
+    }
+}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 9d97dd3..b37b3f0 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -298,6 +298,7 @@
         setIntent(_intent, info);
         setMinDimensions(info);
         touchActiveTime();
+        mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
     }
 
     TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
@@ -329,6 +330,7 @@
         mTaskToReturnTo = HOME_ACTIVITY_TYPE;
         lastTaskDescription = _taskDescription;
         touchActiveTime();
+        mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
     }
 
     private TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
@@ -383,6 +385,7 @@
         mPrivileged = privileged;
         mMinWidth = minWidth;
         mMinHeight = minHeight;
+        mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
     }
 
     void touchActiveTime() {
@@ -861,7 +864,7 @@
             // We normally notify listeners of task stack changes on pause, however pinned stack
             // activities are normally in the paused state so no notification will be sent there
             // before the activity is removed. We send it here so instead.
-            mService.notifyTaskStackChangedLocked();
+            mService.mTaskChangeNotificationController.notifyTaskStackChanged();
         }
 
         if (mActivities.isEmpty()) {
diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
index 52d0928..8948fa3 100644
--- a/services/core/java/com/android/server/pm/BasePermission.java
+++ b/services/core/java/com/android/server/pm/BasePermission.java
@@ -94,4 +94,8 @@
                 == PermissionInfo.PROTECTION_SIGNATURE
                 && (protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
     }
+
+    public boolean isEphemeral() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_EPHEMERAL) != 0;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 974d95d..59c4fb9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4083,6 +4083,11 @@
                 return;
             }
 
+            if (pkg.applicationInfo.isEphemeralApp() && !bp.isEphemeral()) {
+                throw new SecurityException("Cannot grant non-ephemeral permission"
+                        + name + " for package " + packageName);
+            }
+
             if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
                 Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
                 return;
@@ -10109,6 +10114,14 @@
                 continue;
             }
 
+
+            // Limit ephemeral apps to ephemeral allowed permissions.
+            if (pkg.applicationInfo.isEphemeralApp() && !bp.isEphemeral()) {
+                Log.i(TAG, "Denying non-ephemeral permission " + bp.name + " for package "
+                        + pkg.packageName);
+                continue;
+            }
+
             final String perm = bp.name;
             boolean allowedSig = false;
             int grant = GRANT_DENIED;
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index d558b07..38d69ed 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -635,7 +635,11 @@
                 return false; // Shouldn't happen.
             }
 
-            if (!isNewApp && !forceRescan) {
+            // Always scan the settings app, since its version code is the same for DR and MR1.
+            // TODO Fix it properly: b/32554059
+            final boolean isSettings = "com.android.settings".equals(getPackageName());
+
+            if (!isNewApp && !forceRescan && !isSettings) {
                 // Return if the package hasn't changed, ie:
                 // - version code hasn't change
                 // - lastUpdateTime hasn't change
@@ -652,6 +656,11 @@
                     return false;
                 }
             }
+            if (isSettings) {
+                if (ShortcutService.DEBUG) {
+                    Slog.d(TAG, "Always scan settings.");
+                }
+            }
         } finally {
             s.logDurationStat(Stats.PACKAGE_UPDATE_CHECK, start);
         }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 893749e..bde65ed 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -648,12 +648,11 @@
             }
 
             private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
-                DisplayContent displayContent = mWindowManagerService
-                        .getDefaultDisplayContentLocked();
-                WindowList windowList = displayContent.getWindowList();
+                final DisplayContent dc = mWindowManagerService.getDefaultDisplayContentLocked();
+                final ReadOnlyWindowList windowList = dc.getReadOnlyWindowList();
                 final int windowCount = windowList.size();
                 for (int i = 0; i < windowCount; i++) {
-                    WindowState windowState = windowList.get(i);
+                    final WindowState windowState = windowList.get(i);
                     if (windowState.isOnScreen() && windowState.isVisibleLw() &&
                             !windowState.mWinAnimator.mEnterAnimationPending) {
                         outWindows.put(windowState.mLayer, windowState);
@@ -1027,9 +1026,8 @@
                 Region unaccountedSpace = mTempRegion;
                 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
 
-                SparseArray<WindowState> visibleWindows = mTempWindowStates;
+                final SparseArray<WindowState> visibleWindows = mTempWindowStates;
                 populateVisibleWindowsOnScreenLocked(visibleWindows);
-
                 Set<IBinder> addedWindows = mTempBinderSet;
                 addedWindows.clear();
 
@@ -1299,12 +1297,11 @@
         }
 
         private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
-            DisplayContent displayContent = mWindowManagerService
-                    .getDefaultDisplayContentLocked();
-            WindowList windowList = displayContent.getWindowList();
+            final DisplayContent dc = mWindowManagerService.getDefaultDisplayContentLocked();
+            final ReadOnlyWindowList windowList = dc.getReadOnlyWindowList();
             final int windowCount = windowList.size();
             for (int i = 0; i < windowCount; i++) {
-                WindowState windowState = windowList.get(i);
+                final WindowState windowState = windowList.get(i);
                 if (windowState.isVisibleLw()) {
                     outWindows.put(windowState.mLayer, windowState);
                 }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a3f7ac6..e3588ff 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -946,8 +946,7 @@
 
             if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
                     "Removing starting window: " + tStartingWindow);
-            getDisplayContent().getWindowList().remove(tStartingWindow);
-            mService.mWindowsChanged = true;
+            getDisplayContent().removeFromWindowList(tStartingWindow);
             if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
                     "Removing starting " + tStartingWindow + " from " + fromToken);
             fromToken.removeChild(tStartingWindow);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0b39d65..793d2fe 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -29,9 +29,13 @@
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
+import static android.view.View.GONE;
 import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_TOP;
+import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
+import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -40,16 +44,23 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE;
 import static android.view.WindowManager.LayoutParams.NEEDS_MENU_UNSET;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
@@ -69,18 +80,28 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
+import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER;
 import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
+import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.dipToPixel;
 import static com.android.server.wm.WindowManagerService.localLOGV;
+import static com.android.server.wm.WindowManagerService.logSurface;
 import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
 import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
@@ -91,6 +112,7 @@
 import android.annotation.NonNull;
 import android.app.ActivityManager.StackId;
 import android.content.res.Configuration;
+import android.graphics.Bitmap;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -107,13 +129,16 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.IWindow;
-import android.view.WindowManager;
+import android.view.InputChannel;
+import android.view.Surface;
+import android.view.SurfaceControl;
 import android.view.WindowManagerPolicy;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.view.IInputMethodClient;
+import com.android.server.input.InputWindowHandle;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -123,7 +148,11 @@
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 /**
  * Utility class for keeping track of the WindowStates and other pertinent contents of a
@@ -196,6 +225,7 @@
     private boolean mDeferredRemoval;
 
     final DockedStackDividerController mDividerControllerLocked;
+    final PinnedStackController mPinnedStackControllerLocked;
 
     final DimLayerController mDimLayerController;
 
@@ -204,6 +234,14 @@
     /** Used when rebuilding window list to keep track of windows that have been removed. */
     private WindowState[] mRebuildTmp = new WindowState[20];
 
+    /**
+     * Temporary list for comparison. Always clear this after use so we don't end up with
+     * orphaned windows references
+     */
+    private final ArrayList<WindowState> mTmpWindows = new ArrayList<>();
+
+    private final LinkedList<AppWindowToken> mTmpUpdateAllDrawn = new LinkedList();
+
     private final TaskForResizePointSearchResult mTmpTaskForResizePointSearchResult =
             new TaskForResizePointSearchResult();
     private final GetWindowOnDisplaySearchResult mTmpGetWindowOnDisplaySearchResult =
@@ -237,6 +275,7 @@
         mService = service;
         initializeDisplayBaseInfo();
         mDividerControllerLocked = new DockedStackDividerController(service, this);
+        mPinnedStackControllerLocked = new PinnedStackController(service, this);
         mDimLayerController = new DimLayerController(this);
 
         // These are the only direct children we should ever have and they are permanent.
@@ -248,10 +287,6 @@
         return mDisplayId;
     }
 
-    WindowList getWindowList() {
-        return mWindows;
-    }
-
     WindowToken getWindowToken(IBinder binder) {
         return mTokenMap.get(binder);
     }
@@ -307,6 +342,10 @@
         return mDividerControllerLocked;
     }
 
+    PinnedStackController getPinnedStackController() {
+        return mPinnedStackControllerLocked;
+    }
+
     /**
      * Returns true if the specified UID has access to this display.
      */
@@ -345,6 +384,7 @@
         mService.reconfigureDisplayLocked(this);
 
         getDockedDividerController().onConfigurationChanged();
+        getPinnedStackController().onConfigurationChanged();
     }
 
     /**
@@ -539,14 +579,6 @@
         throw new UnsupportedOperationException("See DisplayChildWindowContainer");
     }
 
-    /**
-     * Propagate the new bounds to all child stacks.
-     * @param contentRect The bounds to apply at the top level.
-     */
-    void resize(Rect contentRect) {
-        mContentRect.set(contentRect);
-    }
-
     int taskIdFromPoint(int x, int y) {
         for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
             final TaskStack stack = mTaskStackContainers.get(stackNdx);
@@ -618,9 +650,9 @@
     }
 
     void switchUser() {
-        final WindowList windows = getWindowList();
-        for (int i = 0; i < windows.size(); i++) {
-            final WindowState win = windows.get(i);
+        final int count = mWindows.size();
+        for (int i = 0; i < count; i++) {
+            final WindowState win = mWindows.get(i);
             if (win.isHiddenFromUserLocked()) {
                 if (DEBUG_VISIBILITY) Slog.w(TAG_WM, "user changing, hiding " + win
                         + ", attrs=" + win.mAttrs.type + ", belonging to " + win.mOwnerUid);
@@ -645,7 +677,7 @@
         return mDimLayerController.animateDimLayers();
     }
 
-    void resetDimming() {
+    private void resetDimming() {
         mDimLayerController.resetDimming();
     }
 
@@ -653,7 +685,7 @@
         return mDimLayerController.isDimming();
     }
 
-    void stopDimmingIfNeeded() {
+    private void stopDimmingIfNeeded() {
         mDimLayerController.stopDimmingIfNeeded();
     }
 
@@ -788,6 +820,7 @@
             mDividerControllerLocked.setAdjustedForIme(
                     false /*ime*/, false /*divider*/, dockVisible /*animate*/, imeWin, imeHeight);
         }
+        mPinnedStackControllerLocked.setAdjustedForIme(imeVisible, imeHeight);
     }
 
     void setInputMethodAnimLayerAdjustment(int adj) {
@@ -930,6 +963,8 @@
         mDimLayerController.dump(prefix + "  ", pw);
         pw.println();
         mDividerControllerLocked.dump(prefix + "  ", pw);
+        pw.println();
+        mPinnedStackControllerLocked.dump(prefix + "  ", pw);
 
         if (mInputMethodAnimLayerAdjustment != 0) {
             pw.println(subPrefix
@@ -998,10 +1033,9 @@
 
     boolean canAddToastWindowForUid(int uid) {
         // We allow one toast window per UID being shown at a time.
-        WindowList windows = getWindowList();
-        final int windowCount = windows.size();
+        final int windowCount = mWindows.size();
         for (int i = 0; i < windowCount; i++) {
-            WindowState window = windows.get(i);
+            final WindowState window = mWindows.get(i);
             if (window.mAttrs.type == TYPE_TOAST && window.mOwnerUid == uid
                     && !window.mPermanentlyHidden && !window.mAnimatingExit
                     && !window.mRemoveOnExit) {
@@ -1016,11 +1050,10 @@
             return;
         }
         final int lostFocusUid = oldFocus.mOwnerUid;
-        final WindowList windows = getWindowList();
-        final int windowCount = windows.size();
+        final int windowCount = mWindows.size();
         final Handler handler = mService.mH;
         for (int i = 0; i < windowCount; i++) {
-            final WindowState window = windows.get(i);
+            final WindowState window = mWindows.get(i);
             if (window.mAttrs.type == TYPE_TOAST && window.mOwnerUid == lostFocusUid) {
                 if (!handler.hasMessages(WINDOW_HIDE_TIMEOUT, window)) {
                     handler.sendMessageDelayed(handler.obtainMessage(WINDOW_HIDE_TIMEOUT, window),
@@ -1205,34 +1238,35 @@
     }
 
     void addToWindowList(WindowState win, int index) {
+        mService.mWindowsChanged = true;
         mWindows.add(index, win);
     }
 
     boolean removeFromWindowList(WindowState win) {
+        mService.mWindowsChanged = true;
         return mWindows.remove(win);
     }
 
     private int removeWindowAndChildrenFromWindowList(WindowState win, int interestingPos) {
-        final WindowList windows = getWindowList();
-        int wpos = windows.indexOf(win);
+        int wpos = mWindows.indexOf(win);
         if (wpos < 0) {
             return interestingPos;
         }
 
         if (wpos < interestingPos) interestingPos--;
         if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Temp removing at " + wpos + ": " + this);
-        windows.remove(wpos);
+        mWindows.remove(wpos);
         mService.mWindowsChanged = true;
         int childWinCount = win.mChildren.size();
         while (childWinCount > 0) {
             childWinCount--;
             final WindowState cw = win.mChildren.get(childWinCount);
-            int cpos = windows.indexOf(cw);
+            int cpos = mWindows.indexOf(cw);
             if (cpos >= 0) {
                 if (cpos < interestingPos) interestingPos--;
                 if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM,
                         "Temp removing child at " + cpos + ": " + cw);
-                windows.remove(cpos);
+                mWindows.remove(cpos);
             }
         }
         return interestingPos;
@@ -1282,14 +1316,14 @@
 
     /** Updates the layer assignment of windows on this display. */
     void assignWindowLayers(boolean setLayoutNeeded) {
-        mLayersController.assignWindowLayers(mWindows);
+        mLayersController.assignWindowLayers(mWindows.getReadOnly());
         if (setLayoutNeeded) {
             setLayoutNeeded();
         }
     }
 
     void adjustWallpaperWindows() {
-        if (mWallpaperController.adjustWallpaperWindows(mWindows)) {
+        if (mWallpaperController.adjustWallpaperWindows(mWindows.getReadOnly())) {
             assignWindowLayers(true /*setLayoutNeeded*/);
         }
     }
@@ -1386,6 +1420,160 @@
         Arrays.fill(mRebuildTmp, null);
     }
 
+    /** Rebuilds the display's window list and does a relayout if something changed. */
+    void rebuildAppWindowsAndLayoutIfNeeded() {
+        mTmpWindows.clear();
+        mTmpWindows.addAll(mWindows);
+
+        rebuildAppWindowList();
+
+        // Set displayContent.mLayoutNeeded if window order changed.
+        final int tmpSize = mTmpWindows.size();
+        final int winSize = mWindows.size();
+        int tmpNdx = 0, winNdx = 0;
+        while (tmpNdx < tmpSize && winNdx < winSize) {
+            // Skip over all exiting windows, they've been moved out of order.
+            WindowState tmp;
+            do {
+                tmp = mTmpWindows.get(tmpNdx++);
+            } while (tmpNdx < tmpSize && tmp.mAppToken != null && tmp.mAppToken.mIsExiting);
+
+            WindowState win;
+            do {
+                win = mWindows.get(winNdx++);
+            } while (winNdx < winSize && win.mAppToken != null && win.mAppToken.mIsExiting);
+
+            if (tmp != win) {
+                // Window order changed.
+                setLayoutNeeded();
+                break;
+            }
+        }
+        if (tmpNdx != winNdx) {
+            // One list was different from the other.
+            setLayoutNeeded();
+        }
+        mTmpWindows.clear();
+
+        if (!mService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+                false /*updateInputWindows*/)) {
+            assignWindowLayers(false /* setLayoutNeeded */);
+        }
+
+        mService.mInputMonitor.setUpdateInputWindowsNeededLw();
+        mService.mWindowPlacerLocked.performSurfacePlacement();
+        mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
+    }
+
+    void updateInputWindows(InputMonitor inputMonitor, WindowState inputFocus, boolean inDrag) {
+        final InputConsumerImpl navInputConsumer =
+                mService.mInputMonitor.getInputConsumer(INPUT_CONSUMER_NAVIGATION, mDisplayId);
+        final InputConsumerImpl pipInputConsumer =
+                mService.mInputMonitor.getInputConsumer(INPUT_CONSUMER_PIP, mDisplayId);
+        final InputConsumerImpl wallpaperInputConsumer =
+                mService.mInputMonitor.getInputConsumer(INPUT_CONSUMER_WALLPAPER, mDisplayId);
+        boolean addInputConsumerHandle = navInputConsumer != null;
+        boolean addPipInputConsumerHandle = pipInputConsumer != null;
+        boolean addWallpaperInputConsumerHandle = wallpaperInputConsumer != null;
+        final Rect pipTouchableBounds = addPipInputConsumerHandle ? new Rect() : null;
+        boolean disableWallpaperTouchEvents = false;
+
+        for (int winNdx = mWindows.size() - 1; winNdx >= 0; --winNdx) {
+            final WindowState child = mWindows.get(winNdx);
+            final InputChannel inputChannel = child.mInputChannel;
+            final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
+            if (inputChannel == null || inputWindowHandle == null || child.mRemoved
+                    || child.isAdjustedForMinimizedDock()) {
+                // Skip this window because it cannot possibly receive input.
+                continue;
+            }
+
+            if (addPipInputConsumerHandle
+                    && child.getStackId() == PINNED_STACK_ID
+                    && inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer) {
+                // Update the bounds of the Pip input consumer to match the Pinned stack
+                child.getStack().getBounds(pipTouchableBounds);
+                pipInputConsumer.mWindowHandle.touchableRegion.set(pipTouchableBounds);
+                inputMonitor.addInputWindowHandle(pipInputConsumer.mWindowHandle);
+                addPipInputConsumerHandle = false;
+            }
+
+            if (addInputConsumerHandle
+                    && inputWindowHandle.layer <= navInputConsumer.mWindowHandle.layer) {
+                inputMonitor.addInputWindowHandle(navInputConsumer.mWindowHandle);
+                addInputConsumerHandle = false;
+            }
+
+            if (addWallpaperInputConsumerHandle) {
+                if (child.mAttrs.type == TYPE_WALLPAPER && child.isVisibleLw()) {
+                    // Add the wallpaper input consumer above the first visible wallpaper.
+                    inputMonitor.addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
+                    addWallpaperInputConsumerHandle = false;
+                }
+            }
+
+            final int flags = child.mAttrs.flags;
+            final int privateFlags = child.mAttrs.privateFlags;
+            final int type = child.mAttrs.type;
+
+            final boolean hasFocus = child == inputFocus;
+            final boolean isVisible = child.isVisibleLw();
+            if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) {
+                disableWallpaperTouchEvents = true;
+            }
+            final boolean hasWallpaper = mWallpaperController.isWallpaperTarget(child)
+                    && (privateFlags & PRIVATE_FLAG_KEYGUARD) == 0
+                    && !disableWallpaperTouchEvents;
+
+            // If there's a drag in progress and 'child' is a potential drop target,
+            // make sure it's been told about the drag
+            if (inDrag && isVisible && isDefaultDisplay) {
+                mService.mDragState.sendDragStartedIfNeededLw(child);
+            }
+
+            inputMonitor.addInputWindowHandle(
+                    inputWindowHandle, child, flags, type, isVisible, hasFocus, hasWallpaper);
+        }
+
+        if (addWallpaperInputConsumerHandle) {
+            // No visible wallpaper found, add the wallpaper input consumer at the end.
+            inputMonitor.addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
+        }
+    }
+
+    /** Returns true if a leaked surface was destroyed */
+    boolean destroyLeakedSurfaces() {
+        boolean leakedSurface = false;
+        final int numWindows = mWindows.size();
+        for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
+            final WindowState ws = mWindows.get(winNdx);
+            final WindowStateAnimator wsa = ws.mWinAnimator;
+            if (wsa.mSurfaceController == null) {
+                continue;
+            }
+            if (!mService.mSessions.contains(wsa.mSession)) {
+                Slog.w(TAG_WM, "LEAKED SURFACE (session doesn't exist): "
+                        + ws + " surface=" + wsa.mSurfaceController
+                        + " token=" + ws.mToken
+                        + " pid=" + ws.mSession.mPid
+                        + " uid=" + ws.mSession.mUid);
+                wsa.destroySurface();
+                mService.mForceRemoves.add(ws);
+                leakedSurface = true;
+            } else if (ws.mAppToken != null && ws.mAppToken.clientHidden) {
+                Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): "
+                        + ws + " surface=" + wsa.mSurfaceController
+                        + " token=" + ws.mAppToken
+                        + " saved=" + ws.hasSavedSurface());
+                if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", false);
+                wsa.destroySurface();
+                leakedSurface = true;
+            }
+        }
+
+        return leakedSurface;
+    }
+
     /** Return the list of Windows on this display associated with the input token. */
     WindowList getTokenWindowsOnDisplay(WindowToken token) {
         final WindowList windowList = new WindowList();
@@ -1467,8 +1655,6 @@
         }
 
         // TODO(multidisplay): IMEs are only supported on the default display.
-        WindowList windows = mWindows;
-
         int imPos = findDesiredInputMethodWindowIndex(true);
         if (imPos >= 0) {
             // In this case, the input method windows are to be placed
@@ -1476,8 +1662,8 @@
 
             // First check to see if the input method windows are already
             // located here, and contiguous.
-            final int N = windows.size();
-            final WindowState firstImWin = imPos < N ? windows.get(imPos) : null;
+            final int N = mWindows.size();
+            final WindowState firstImWin = imPos < N ? mWindows.get(imPos) : null;
 
             // Figure out the actual input method window that should be
             // at the bottom of their stack.
@@ -1492,7 +1678,7 @@
                 // First find the top IM window.
                 int pos = imPos+1;
                 while (pos < N) {
-                    if (!(windows.get(pos)).mIsImWindow) {
+                    if (!(mWindows.get(pos)).mIsImWindow) {
                         break;
                     }
                     pos++;
@@ -1500,7 +1686,7 @@
                 pos++;
                 // Now there should be no more input method windows above.
                 while (pos < N) {
-                    if ((windows.get(pos)).mIsImWindow) {
+                    if ((mWindows.get(pos)).mIsImWindow) {
                         break;
                     }
                     pos++;
@@ -1513,17 +1699,17 @@
             if (imWin != null) {
                 if (DEBUG_INPUT_METHOD) {
                     Slog.v(TAG_WM, "Moving IM from " + imPos);
-                    logWindowList(windows, "  ");
+                    logWindowList(mWindows, "  ");
                 }
                 imPos = removeWindowAndChildrenFromWindowList(imWin, imPos);
                 if (DEBUG_INPUT_METHOD) {
                     Slog.v(TAG_WM, "List after removing with new pos " + imPos + ":");
-                    logWindowList(windows, "  ");
+                    logWindowList(mWindows, "  ");
                 }
                 imWin.reAddWindow(imPos);
                 if (DEBUG_INPUT_METHOD) {
                     Slog.v(TAG_WM, "List after moving IM to " + imPos + ":");
-                    logWindowList(windows, "  ");
+                    logWindowList(mWindows, "  ");
                 }
                 if (DN > 0) moveInputMethodDialogs(imPos+1);
             } else {
@@ -1540,7 +1726,7 @@
                 reAddToWindowList(imWin);
                 if (DEBUG_INPUT_METHOD) {
                     Slog.v(TAG_WM, "List with no IM target:");
-                    logWindowList(windows, "  ");
+                    logWindowList(mWindows, "  ");
                 }
                 if (DN > 0) moveInputMethodDialogs(-1);
             } else {
@@ -1565,11 +1751,10 @@
         // TODO(multidisplay): Needs some serious rethought when the target and IME are not on the
         // same display. Or even when the current IME/target are not on the same screen as the next
         // IME/target. For now only look for input windows on the main screen.
-        final WindowList windows = getWindowList();
         WindowState w = null;
         int i;
-        for (i = windows.size() - 1; i >= 0; --i) {
-            WindowState win = windows.get(i);
+        for (i = mWindows.size() - 1; i >= 0; --i) {
+            final WindowState win = mWindows.get(i);
 
             if (DEBUG_INPUT_METHOD && willMove) Slog.i(TAG_WM, "Checking window @" + i
                     + " " + win + " fl=0x" + Integer.toHexString(win.mAttrs.flags));
@@ -1582,7 +1767,7 @@
                 // is not actually looking to move the IME, look down below for a real window to
                 // target...
                 if (!willMove && w.mAttrs.type == TYPE_APPLICATION_STARTING && i > 0) {
-                    WindowState wb = windows.get(i-1);
+                    final WindowState wb = mWindows.get(i-1);
                     if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) {
                         i--;
                         w = wb;
@@ -1607,7 +1792,7 @@
                 && curTarget.isClosing()
                 && (w == null || curTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer)) {
             if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Current target higher, not changing");
-            return windows.indexOf(curTarget) + 1;
+            return mWindows.indexOf(curTarget) + 1;
         }
 
         if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Desired input method target="
@@ -1623,7 +1808,7 @@
                 WindowState highestTarget = null;
                 int highestPos = 0;
                 if (token.mAppAnimator.animating || token.mAppAnimator.animation != null) {
-                    WindowList curWindows = token.getDisplayContent().getWindowList();
+                    WindowList curWindows = token.getDisplayContent().mWindows;
                     int pos = curWindows.indexOf(curTarget);
                     while (pos >= 0) {
                         WindowState win = curWindows.get(pos);
@@ -1688,7 +1873,7 @@
             // docked divider. Unless it is already above the divider.
             final WindowState dockedDivider = mDividerControllerLocked.getWindow();
             if (dockedDivider != null && dockedDivider.isVisibleLw()) {
-                int dividerIndex = windows.indexOf(dockedDivider);
+                int dividerIndex = mWindows.indexOf(dockedDivider);
                 if (dividerIndex > 0 && dividerIndex > i) {
                     return dividerIndex + 1;
                 }
@@ -1741,7 +1926,6 @@
 
     boolean getNeedsMenu(WindowState win, WindowManagerPolicy.WindowState bottom) {
         int index = -1;
-        WindowList windows = getWindowList();
         while (true) {
             if (win.mAttrs.needsMenuKey != NEEDS_MENU_UNSET) {
                 return win.mAttrs.needsMenuKey == NEEDS_MENU_SET_TRUE;
@@ -1754,13 +1938,13 @@
             // The current window hasn't specified whether menu key is needed; look behind it.
             // First, we may need to determine the starting position.
             if (index < 0) {
-                index = windows.indexOf(win);
+                index = mWindows.indexOf(win);
             }
             index--;
             if (index < 0) {
                 return false;
             }
-            win = windows.get(index);
+            win = mWindows.get(index);
         }
     }
 
@@ -1769,7 +1953,7 @@
         mLayoutNeeded = true;
     }
 
-    void clearLayoutNeeded() {
+    private void clearLayoutNeeded() {
         if (DEBUG_LAYOUT) Slog.w(TAG_WM, "clearLayoutNeeded: callers=" + Debug.getCallers(3));
         mLayoutNeeded = false;
     }
@@ -1852,9 +2036,8 @@
 
     private void dumpWindows() {
         Slog.v(TAG_WM, " Display #" + mDisplayId);
-        final WindowList windows = getWindowList();
-        for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-            Slog.v(TAG_WM, "  #" + winNdx + ": " + windows.get(winNdx));
+        for (int winNdx = mWindows.size() - 1; winNdx >= 0; --winNdx) {
+            Slog.v(TAG_WM, "  #" + winNdx + ": " + mWindows.get(winNdx));
         }
     }
 
@@ -2424,6 +2607,673 @@
         }
     }
 
+    ReadOnlyWindowList getReadOnlyWindowList() {
+        return mWindows.getReadOnly();
+    }
+
+    void getWindows(WindowList output) {
+        output.addAll(mWindows);
+    }
+
+    // TODO: Super crazy long method that should be broken down...
+    boolean applySurfaceChangesTransaction(boolean recoveringMemory) {
+
+        boolean focusDisplayed = false;
+        boolean displayHasContent = false;
+        float preferredRefreshRate = 0;
+        int preferredModeId = 0;
+
+
+        final int dw = mDisplayInfo.logicalWidth;
+        final int dh = mDisplayInfo.logicalHeight;
+        final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
+
+        mTmpUpdateAllDrawn.clear();
+
+        int repeats = 0;
+        do {
+            repeats++;
+            if (repeats > 6) {
+                Slog.w(TAG, "Animation repeat aborted after too many iterations");
+                clearLayoutNeeded();
+                break;
+            }
+
+            if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("On entry to LockedInner",
+                    pendingLayoutChanges);
+
+            if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+                adjustWallpaperWindows();
+            }
+
+            if (isDefaultDisplay && (pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
+                if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
+                if (mService.updateOrientationFromAppTokensLocked(true, mDisplayId)) {
+                    setLayoutNeeded();
+                    mService.mH.sendEmptyMessage(SEND_NEW_CONFIGURATION);
+                }
+            }
+
+            if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+                setLayoutNeeded();
+            }
+
+            // FIRST LOOP: Perform a layout, if needed.
+            if (repeats < LAYOUT_REPEAT_THRESHOLD) {
+                performLayout(repeats == 1, false /* updateInputWindows */);
+            } else {
+                Slog.w(TAG, "Layout repeat skipped after too many iterations");
+            }
+
+            // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think it is animating.
+            pendingLayoutChanges = 0;
+
+            if (isDefaultDisplay) {
+                mService.mPolicy.beginPostLayoutPolicyLw(dw, dh);
+                for (int i = mWindows.size() - 1; i >= 0; i--) {
+                    final WindowState w = mWindows.get(i);
+                    if (w.mHasSurface) {
+                        mService.mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs, w.getParentWindow());
+                    }
+                }
+                pendingLayoutChanges |= mService.mPolicy.finishPostLayoutPolicyLw();
+                if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
+                        "after finishPostLayoutPolicyLw", pendingLayoutChanges);
+            }
+        } while (pendingLayoutChanges != 0);
+
+        RootWindowContainer root = mService.mRoot;
+        boolean obscured = false;
+        boolean syswin = false;
+        resetDimming();
+
+        // Only used if default window
+        final boolean someoneLosingFocus = !mService.mLosingFocus.isEmpty();
+
+        for (int i = mWindows.size() - 1; i >= 0; i--) {
+            final WindowState w = mWindows.get(i);
+            final Task task = w.getTask();
+            final boolean obscuredChanged = w.mObscured != obscured;
+
+            // Update effect.
+            w.mObscured = obscured;
+            if (!obscured) {
+                final boolean isDisplayed = w.isDisplayedLw();
+
+                if (isDisplayed && w.isObscuringFullscreen(mDisplayInfo)) {
+                    // This window completely covers everything behind it, so we want to leave all
+                    // of them as undimmed (for performance reasons).
+                    root.mObscuringWindow = w;
+                    obscured = true;
+                }
+
+                displayHasContent |= root.handleNotObscuredLocked(w, obscured, syswin);
+
+                if (w.mHasSurface && isDisplayed) {
+                    final int type = w.mAttrs.type;
+                    if (type == TYPE_SYSTEM_DIALOG || type == TYPE_SYSTEM_ERROR
+                            || (w.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
+                        syswin = true;
+                    }
+                    if (preferredRefreshRate == 0 && w.mAttrs.preferredRefreshRate != 0) {
+                        preferredRefreshRate = w.mAttrs.preferredRefreshRate;
+                    }
+                    if (preferredModeId == 0 && w.mAttrs.preferredDisplayModeId != 0) {
+                        preferredModeId = w.mAttrs.preferredDisplayModeId;
+                    }
+                }
+            }
+
+            w.applyDimLayerIfNeeded();
+
+            if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
+                    && mWallpaperController.isWallpaperTarget(w)) {
+                // This is the wallpaper target and its obscured state changed... make sure the
+                // current wallpaper's visibility has been updated accordingly.
+                mWallpaperController.updateWallpaperVisibility();
+            }
+
+            w.handleWindowMovedIfNeeded();
+
+            final WindowStateAnimator winAnimator = w.mWinAnimator;
+
+            //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
+            w.mContentChanged = false;
+
+            // Moved from updateWindowsAndWallpaperLocked().
+            if (w.mHasSurface) {
+                // Take care of the window being ready to display.
+                final boolean committed = winAnimator.commitFinishDrawingLocked();
+                if (isDefaultDisplay && committed) {
+                    if (w.mAttrs.type == TYPE_DREAM) {
+                        // HACK: When a dream is shown, it may at that point hide the lock screen.
+                        // So we need to redo the layout to let the phone window manager make this
+                        // happen.
+                        pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
+                        if (DEBUG_LAYOUT_REPEATS) {
+                            surfacePlacer.debugLayoutRepeats(
+                                    "dream and commitFinishDrawingLocked true",
+                                    pendingLayoutChanges);
+                        }
+                    }
+                    if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
+                        if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
+                                "First draw done in potential wallpaper target " + w);
+                        root.mWallpaperMayChange = true;
+                        pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+                        if (DEBUG_LAYOUT_REPEATS) {
+                            surfacePlacer.debugLayoutRepeats(
+                                    "wallpaper and commitFinishDrawingLocked true",
+                                    pendingLayoutChanges);
+                        }
+                    }
+                }
+                if (!winAnimator.isAnimationStarting() && !winAnimator.isWaitingForOpening()) {
+                    // Updates the shown frame before we set up the surface. This is needed
+                    // because the resizing could change the top-left position (in addition to
+                    // size) of the window. setSurfaceBoundariesLocked uses mShownPosition to
+                    // position the surface.
+                    //
+                    // If an animation is being started, we can't call this method because the
+                    // animation hasn't processed its initial transformation yet, but in general
+                    // we do want to update the position if the window is animating.
+                    winAnimator.computeShownFrameLocked();
+                }
+                winAnimator.setSurfaceBoundariesLocked(recoveringMemory);
+            }
+
+            final AppWindowToken atoken = w.mAppToken;
+            if (atoken != null) {
+                final boolean updateAllDrawn = atoken.updateDrawnWindowStates(w);
+                if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(atoken)) {
+                    mTmpUpdateAllDrawn.add(atoken);
+                }
+            }
+
+            if (isDefaultDisplay && someoneLosingFocus && w == mService.mCurrentFocus
+                    && w.isDisplayedLw()) {
+                focusDisplayed = true;
+            }
+
+            w.updateResizingWindowIfNeeded();
+        }
+
+        mService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
+                displayHasContent,
+                preferredRefreshRate,
+                preferredModeId,
+                true /* inTraversal, must call performTraversalInTrans... below */);
+
+        stopDimmingIfNeeded();
+
+        while (!mTmpUpdateAllDrawn.isEmpty()) {
+            final AppWindowToken atoken = mTmpUpdateAllDrawn.removeLast();
+            // See if any windows have been drawn, so they (and others associated with them)
+            // can now be shown.
+            atoken.updateAllDrawn(this);
+        }
+
+        return focusDisplayed;
+    }
+
+    void performLayout(boolean initial, boolean updateInputWindows) {
+        if (!isLayoutNeeded()) {
+            return;
+        }
+        clearLayoutNeeded();
+
+        final int dw = mDisplayInfo.logicalWidth;
+        final int dh = mDisplayInfo.logicalHeight;
+
+        int i;
+
+        if (DEBUG_LAYOUT) {
+            Slog.v(TAG, "-------------------------------------");
+            Slog.v(TAG, "performLayout: needed=" + isLayoutNeeded() + " dw=" + dw + " dh=" + dh);
+        }
+
+        mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mService.mRotation,
+                getConfiguration().uiMode);
+        if (isDefaultDisplay) {
+            // Not needed on non-default displays.
+            mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
+            mService.mScreenRect.set(0, 0, dw, dh);
+        }
+
+        mService.mPolicy.getContentRectLw(mContentRect);
+
+        int seq = mService.mLayoutSeq + 1;
+        if (seq < 0) seq = 0;
+        mService.mLayoutSeq = seq;
+
+        boolean behindDream = false;
+
+        // First perform layout of any root windows (not attached to another window).
+        int topAttached = -1;
+        for (i = mWindows.size() - 1; i >= 0; i--) {
+            final WindowState win = mWindows.get(i);
+
+            // Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
+            // wasting time and funky changes while a window is animating away.
+            final boolean gone = (behindDream && mService.mPolicy.canBeForceHidden(win, win.mAttrs))
+                    || win.isGoneForLayoutLw();
+
+            if (DEBUG_LAYOUT && !win.mLayoutAttached) {
+                Slog.v(TAG, "1ST PASS " + win + ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame
+                        + " mLayoutAttached=" + win.mLayoutAttached
+                        + " screen changed=" + win.isConfigChanged());
+                final AppWindowToken atoken = win.mAppToken;
+                if (gone) Slog.v(TAG, "  GONE: mViewVisibility=" + win.mViewVisibility
+                        + " mRelayoutCalled=" + win.mRelayoutCalled + " hidden=" + win.mToken.hidden
+                        + " hiddenRequested=" + (atoken != null && atoken.hiddenRequested)
+                        + " parentHidden=" + win.isParentWindowHidden());
+                else Slog.v(TAG, "  VIS: mViewVisibility=" + win.mViewVisibility
+                        + " mRelayoutCalled=" + win.mRelayoutCalled + " hidden=" + win.mToken.hidden
+                        + " hiddenRequested=" + (atoken != null && atoken.hiddenRequested)
+                        + " parentHidden=" + win.isParentWindowHidden());
+            }
+
+            // If this view is GONE, then skip it -- keep the current frame, and let the caller know
+            // so they can ignore it if they want.  (We do the normal layout for INVISIBLE windows,
+            // since that means "perform layout as normal, just don't display").
+            if (!gone || !win.mHaveFrame || win.mLayoutNeeded
+                    || ((win.isConfigChanged() || win.setReportResizeHints())
+                    && !win.isGoneForLayoutLw() &&
+                    ((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 ||
+                            (win.mHasSurface && win.mAppToken != null &&
+                                    win.mAppToken.layoutConfigChanges)))) {
+                if (!win.mLayoutAttached) {
+                    if (initial) {
+                        //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
+                        win.mContentChanged = false;
+                    }
+                    if (win.mAttrs.type == TYPE_DREAM) {
+                        // Don't layout windows behind a dream, so that if it does stuff like hide
+                        // the status bar we won't get a bad transition when it goes away.
+                        behindDream = true;
+                    }
+                    win.mLayoutNeeded = false;
+                    win.prelayout();
+                    mService.mPolicy.layoutWindowLw(win, null);
+                    win.mLayoutSeq = seq;
+
+                    // Window frames may have changed. Update dim layer with the new bounds.
+                    final Task task = win.getTask();
+                    if (task != null) {
+                        mDimLayerController.updateDimLayer(task);
+                    }
+
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + win.mFrame
+                            + " mContainingFrame=" + win.mContainingFrame
+                            + " mDisplayFrame=" + win.mDisplayFrame);
+                } else {
+                    if (topAttached < 0) topAttached = i;
+                }
+            }
+        }
+
+        boolean attachedBehindDream = false;
+
+        // Now perform layout of attached windows, which usually depend on the position of the
+        // window they are attached to. XXX does not deal with windows that are attached to windows
+        // that are themselves attached.
+        for (i = topAttached; i >= 0; i--) {
+            final WindowState win = mWindows.get(i);
+
+            if (win.mLayoutAttached) {
+                if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + win + " mHaveFrame=" + win.mHaveFrame
+                        + " mViewVisibility=" + win.mViewVisibility
+                        + " mRelayoutCalled=" + win.mRelayoutCalled);
+                // If this view is GONE, then skip it -- keep the current frame, and let the caller
+                // know so they can ignore it if they want.  (We do the normal layout for INVISIBLE
+                // windows, since that means "perform layout as normal, just don't display").
+                if (attachedBehindDream && mService.mPolicy.canBeForceHidden(win, win.mAttrs)) {
+                    continue;
+                }
+                if ((win.mViewVisibility != GONE && win.mRelayoutCalled) || !win.mHaveFrame
+                        || win.mLayoutNeeded) {
+                    if (initial) {
+                        //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
+                        win.mContentChanged = false;
+                    }
+                    win.mLayoutNeeded = false;
+                    win.prelayout();
+                    mService.mPolicy.layoutWindowLw(win, win.getParentWindow());
+                    win.mLayoutSeq = seq;
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + win.mFrame
+                            + " mContainingFrame=" + win.mContainingFrame
+                            + " mDisplayFrame=" + win.mDisplayFrame);
+                }
+            } else if (win.mAttrs.type == TYPE_DREAM) {
+                // Don't layout windows behind a dream, so that if it does stuff like hide the
+                // status bar we won't get a bad transition when it goes away.
+                attachedBehindDream = behindDream;
+            }
+        }
+
+        // Window frames may have changed. Tell the input dispatcher about it.
+        mService.mInputMonitor.layoutInputConsumers(dw, dh);
+        mService.mInputMonitor.setUpdateInputWindowsNeededLw();
+        if (updateInputWindows) {
+            mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
+        }
+
+        mService.mPolicy.finishLayoutLw();
+        mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
+    }
+
+    /**
+     * Takes a snapshot of the display.  In landscape mode this grabs the whole screen.
+     * In portrait mode, it grabs the full screenshot.
+     *
+     * @param width the width of the target bitmap
+     * @param height the height of the target bitmap
+     * @param includeFullDisplay true if the screen should not be cropped before capture
+     * @param frameScale the scale to apply to the frame, only used when width = -1 and height = -1
+     * @param config of the output bitmap
+     * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
+     */
+    Bitmap screenshotApplications(IBinder appToken, int displayId, int width, int height,
+            boolean includeFullDisplay, float frameScale, Bitmap.Config config,
+            boolean wallpaperOnly) {
+        int dw = mDisplayInfo.logicalWidth;
+        int dh = mDisplayInfo.logicalHeight;
+        if (dw == 0 || dh == 0) {
+            if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
+                    + ": returning null. logical widthxheight=" + dw + "x" + dh);
+            return null;
+        }
+
+        Bitmap bm = null;
+
+        int maxLayer = 0;
+        final Rect frame = new Rect();
+        final Rect stackBounds = new Rect();
+
+        boolean screenshotReady;
+        int minLayer;
+        if (appToken == null && !wallpaperOnly) {
+            screenshotReady = true;
+            minLayer = 0;
+        } else {
+            screenshotReady = false;
+            minLayer = Integer.MAX_VALUE;
+        }
+
+        WindowState appWin = null;
+
+        boolean includeImeInScreenshot;
+        synchronized(mService.mWindowMap) {
+            final AppWindowToken imeTargetAppToken = mService.mInputMethodTarget != null
+                    ? mService.mInputMethodTarget.mAppToken : null;
+            // We only include the Ime in the screenshot if the app we are screenshoting is the IME
+            // target and isn't in multi-window mode. We don't screenshot the IME in multi-window
+            // mode because the frame of the IME might not overlap with that of the app.
+            // E.g. IME target app at the top in split-screen mode and the IME at the bottom
+            // overlapping with the bottom app.
+            includeImeInScreenshot = imeTargetAppToken != null
+                    && imeTargetAppToken.appToken != null
+                    && imeTargetAppToken.appToken.asBinder() == appToken
+                    && !mService.mInputMethodTarget.isInMultiWindowMode();
+        }
+
+        final int aboveAppLayer = (mService.mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1)
+                * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
+
+        synchronized(mService.mWindowMap) {
+            // Figure out the part of the screen that is actually the app.
+            appWin = null;
+            for (int i = mWindows.size() - 1; i >= 0; i--) {
+                final WindowState ws = mWindows.get(i);
+                if (!ws.mHasSurface) {
+                    continue;
+                }
+                if (ws.mLayer >= aboveAppLayer) {
+                    continue;
+                }
+                if (wallpaperOnly && !ws.mIsWallpaper) {
+                    continue;
+                }
+                if (ws.mIsImWindow) {
+                    if (!includeImeInScreenshot) {
+                        continue;
+                    }
+                } else if (ws.mIsWallpaper) {
+                    // If this is the wallpaper layer and we're only looking for the wallpaper layer
+                    // then the target window state is this one.
+                    if (wallpaperOnly) {
+                        appWin = ws;
+                    }
+
+                    if (appWin == null) {
+                        // We have not ran across the target window yet, so it is probably behind
+                        // the wallpaper. This can happen when the keyguard is up and all windows
+                        // are moved behind the wallpaper. We don't want to include the wallpaper
+                        // layer in the screenshot as it will cover-up the layer of the target
+                        // window.
+                        continue;
+                    }
+                    // Fall through. The target window is in front of the wallpaper. For this
+                    // case we want to include the wallpaper layer in the screenshot because
+                    // the target window might have some transparent areas.
+                } else if (appToken != null) {
+                    if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
+                        // This app window is of no interest if it is not associated with the
+                        // screenshot app.
+                        continue;
+                    }
+                    appWin = ws;
+                }
+
+                // Include this window.
+
+                final WindowStateAnimator winAnim = ws.mWinAnimator;
+                int layer = winAnim.mSurfaceController.getLayer();
+                if (maxLayer < layer) {
+                    maxLayer = layer;
+                }
+                if (minLayer > layer) {
+                    minLayer = layer;
+                }
+
+                // Don't include wallpaper in bounds calculation
+                if (!includeFullDisplay && !ws.mIsWallpaper) {
+                    final Rect wf = ws.mFrame;
+                    final Rect cr = ws.mContentInsets;
+                    int left = wf.left + cr.left;
+                    int top = wf.top + cr.top;
+                    int right = wf.right - cr.right;
+                    int bottom = wf.bottom - cr.bottom;
+                    frame.union(left, top, right, bottom);
+                    ws.getVisibleBounds(stackBounds);
+                    if (!Rect.intersects(frame, stackBounds)) {
+                        // Set frame empty if there's no intersection.
+                        frame.setEmpty();
+                    }
+                }
+
+                final boolean foundTargetWs =
+                        (ws.mAppToken != null && ws.mAppToken.token == appToken)
+                                || (appWin != null && wallpaperOnly);
+                if (foundTargetWs && ws.isDisplayedLw() && winAnim.getShown()) {
+                    screenshotReady = true;
+                }
+
+                if (ws.isObscuringFullscreen(mDisplayInfo)){
+                    break;
+                }
+            }
+
+            if (appToken != null && appWin == null) {
+                // Can't find a window to snapshot.
+                if (DEBUG_SCREENSHOT) Slog.i(TAG_WM,
+                        "Screenshot: Couldn't find a surface matching " + appToken);
+                return null;
+            }
+
+            if (!screenshotReady) {
+                Slog.i(TAG_WM, "Failed to capture screenshot of " + appToken +
+                        " appWin=" + (appWin == null ? "null" : (appWin + " drawState=" +
+                        appWin.mWinAnimator.mDrawState)));
+                return null;
+            }
+
+            // Screenshot is ready to be taken. Everything from here below will continue
+            // through the bottom of the loop and return a value. We only stay in the loop
+            // because we don't want to release the mWindowMap lock until the screenshot is
+            // taken.
+
+            if (maxLayer == 0) {
+                if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
+                        + ": returning null maxLayer=" + maxLayer);
+                return null;
+            }
+
+            if (!includeFullDisplay) {
+                // Constrain frame to the screen size.
+                if (!frame.intersect(0, 0, dw, dh)) {
+                    frame.setEmpty();
+                }
+            } else {
+                // Caller just wants entire display.
+                frame.set(0, 0, dw, dh);
+            }
+            if (frame.isEmpty()) {
+                return null;
+            }
+
+            if (width < 0) {
+                width = (int) (frame.width() * frameScale);
+            }
+            if (height < 0) {
+                height = (int) (frame.height() * frameScale);
+            }
+
+            // Tell surface flinger what part of the image to crop. Take the top
+            // right part of the application, and crop the larger dimension to fit.
+            Rect crop = new Rect(frame);
+            if (width / (float) frame.width() < height / (float) frame.height()) {
+                int cropWidth = (int)((float)width / (float)height * frame.height());
+                crop.right = crop.left + cropWidth;
+            } else {
+                int cropHeight = (int)((float)height / (float)width * frame.width());
+                crop.bottom = crop.top + cropHeight;
+            }
+
+            // The screenshot API does not apply the current screen rotation.
+            int rot = mDisplay.getRotation();
+
+            if (rot == ROTATION_90 || rot == ROTATION_270) {
+                rot = (rot == ROTATION_90) ? ROTATION_270 : ROTATION_90;
+            }
+
+            // Surfaceflinger is not aware of orientation, so convert our logical
+            // crop to surfaceflinger's portrait orientation.
+            convertCropForSurfaceFlinger(crop, rot, dw, dh);
+
+            if (DEBUG_SCREENSHOT) {
+                Slog.i(TAG_WM, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
+                        + maxLayer + " appToken=" + appToken);
+                for (int i = 0; i < mWindows.size(); i++) {
+                    final WindowState win = mWindows.get(i);
+                    final WindowSurfaceController controller = win.mWinAnimator.mSurfaceController;
+                    Slog.i(TAG_WM, win + ": " + win.mLayer
+                            + " animLayer=" + win.mWinAnimator.mAnimLayer
+                            + " surfaceLayer=" + ((controller == null)
+                            ? "null" : controller.getLayer()));
+                }
+            }
+
+            final ScreenRotationAnimation screenRotationAnimation =
+                    mService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
+            final boolean inRotation = screenRotationAnimation != null &&
+                    screenRotationAnimation.isAnimating();
+            if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM,
+                    "Taking screenshot while rotating");
+
+            // We force pending transactions to flush before taking
+            // the screenshot by pushing an empty synchronous transaction.
+            SurfaceControl.openTransaction();
+            SurfaceControl.closeTransactionSync();
+
+            bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer,
+                    inRotation, rot);
+            if (bm == null) {
+                Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
+                        + ") to layer " + maxLayer);
+                return null;
+            }
+        }
+
+        if (DEBUG_SCREENSHOT) {
+            // TEST IF IT's ALL BLACK
+            int[] buffer = new int[bm.getWidth() * bm.getHeight()];
+            bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
+            boolean allBlack = true;
+            final int firstColor = buffer[0];
+            for (int i = 0; i < buffer.length; i++) {
+                if (buffer[i] != firstColor) {
+                    allBlack = false;
+                    break;
+                }
+            }
+            if (allBlack) {
+                Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" +
+                        Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +
+                        (appWin != null ?
+                                appWin.mWinAnimator.mSurfaceController.getLayer() : "null") +
+                        " minLayer=" + minLayer + " maxLayer=" + maxLayer);
+            }
+        }
+
+        // Create a copy of the screenshot that is immutable and backed in ashmem.
+        // This greatly reduces the overhead of passing the bitmap between processes.
+        Bitmap ret = bm.createAshmemBitmap(config);
+        bm.recycle();
+        return ret;
+    }
+
+    // TODO: Can this use createRotationMatrix()?
+    private static void convertCropForSurfaceFlinger(Rect crop, int rot, int dw, int dh) {
+        if (rot == Surface.ROTATION_90) {
+            final int tmp = crop.top;
+            crop.top = dw - crop.right;
+            crop.right = crop.bottom;
+            crop.bottom = dw - crop.left;
+            crop.left = tmp;
+        } else if (rot == Surface.ROTATION_180) {
+            int tmp = crop.top;
+            crop.top = dh - crop.bottom;
+            crop.bottom = dh - tmp;
+            tmp = crop.right;
+            crop.right = dw - crop.left;
+            crop.left = dw - tmp;
+        } else if (rot == Surface.ROTATION_270) {
+            final int tmp = crop.top;
+            crop.top = crop.left;
+            crop.left = dh - crop.bottom;
+            crop.bottom = crop.right;
+            crop.right = dh - tmp;
+        }
+    }
+
+    void onSeamlessRotationTimeout() {
+        boolean layoutNeeded = false;
+        for (int i = mWindows.size() - 1; i >= 0; i--) {
+            final WindowState w = mWindows.get(i);
+            if (!w.mSeamlesslyRotated) {
+                continue;
+            }
+            layoutNeeded = true;
+            w.setDisplayLayoutNeeded();
+            mService.markForSeamlessRotation(w, false);
+        }
+
+        if (layoutNeeded) {
+            mService.mWindowPlacerLocked.performSurfacePlacement();
+        }
+    }
+
     static final class GetWindowOnDisplaySearchResult {
         boolean reachedToken;
         WindowState foundWindow;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index f75f224..530fb1b 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -268,7 +268,7 @@
     }
 
     private void resetDragResizingChangeReported() {
-        final WindowList windowList = mDisplayContent.getWindowList();
+        final ReadOnlyWindowList windowList = mDisplayContent.getReadOnlyWindowList();
         for (int i = windowList.size() - 1; i >= 0; i--) {
             windowList.get(i).resetDragResizingChangeReported();
         }
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 588bb76..d52168c 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -271,7 +271,7 @@
             Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
         }
 
-        final WindowList windows = mDisplayContent.getWindowList();
+        final ReadOnlyWindowList windows = mDisplayContent.getReadOnlyWindowList();
         final int N = windows.size();
         for (int i = 0; i < N; i++) {
             sendDragStartedLw(windows.get(i), touchX, touchY, mDataDescription);
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 6a06ef3..a8eb75c 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -19,6 +19,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
@@ -109,7 +110,7 @@
         mService = service;
     }
 
-    void addInputConsumer(String name, InputConsumerImpl consumer) {
+    private void addInputConsumer(String name, InputConsumerImpl consumer) {
         mInputConsumers.put(name, consumer);
         updateInputWindowsLw(true /* force */);
     }
@@ -130,8 +131,9 @@
         return false;
     }
 
-    InputConsumerImpl getInputConsumer(String name) {
-        return mInputConsumers.get(name);
+    InputConsumerImpl getInputConsumer(String name, int displayId) {
+        // TODO(multi-display): Allow input consumers on non-default displays?
+        return (displayId == DEFAULT_DISPLAY) ? mInputConsumers.get(name) : null;
     }
 
     void layoutInputConsumers(int dw, int dh) {
@@ -165,8 +167,7 @@
             case INPUT_CONSUMER_PIP:
                 // The touchable region of the Pip input window is cropped to the bounds of the
                 // stack, and we need FLAG_NOT_TOUCH_MODAL to ensure other events fall through
-                consumer.mWindowHandle.layoutParamsFlags |=
-                        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+                consumer.mWindowHandle.layoutParamsFlags |= FLAG_NOT_TOUCH_MODAL;
                 break;
         }
         addInputConsumer(name, consumer);
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
new file mode 100644
index 0000000..a488d52
--- /dev/null
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.animation.ValueAnimator;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.Size;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.IPinnedStackController;
+import android.view.IPinnedStackListener;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.policy.PipMotionHelper;
+import com.android.internal.policy.PipSnapAlgorithm;
+
+import java.io.PrintWriter;
+
+/**
+ * Holds the common state of the pinned stack between the system and SystemUI.
+ */
+class PinnedStackController {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedStackController" : TAG_WM;
+
+    private final WindowManagerService mService;
+    private final DisplayContent mDisplayContent;
+    private final Handler mHandler = new Handler();
+
+    private IPinnedStackListener mPinnedStackListener;
+    private final PinnedStackListenerDeathHandler mPinnedStackListenerDeathHandler =
+            new PinnedStackListenerDeathHandler();
+
+    private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback();
+    private final PipSnapAlgorithm mSnapAlgorithm;
+    private final PipMotionHelper mMotionHelper;
+
+    // States that affect how the PIP can be manipulated
+    private boolean mInInteractiveMode;
+    private boolean mIsImeShowing;
+    private int mImeHeight;
+    private final Rect mPreImeShowingBounds = new Rect();
+    private ValueAnimator mBoundsAnimator = null;
+
+    // The size and position information that describes where the pinned stack will go by default.
+    private int mDefaultStackGravity;
+    private Size mDefaultStackSize;
+    private Point mScreenEdgeInsets;
+
+    // Temp vars for calculation
+    private final DisplayMetrics mTmpMetrics = new DisplayMetrics();
+    private final Rect mTmpInsets = new Rect();
+
+    /**
+     * The callback object passed to listeners for them to notify the controller of state changes.
+     */
+    private class PinnedStackControllerCallback extends IPinnedStackController.Stub {
+
+        @Override
+        public void setInInteractiveMode(final boolean inInteractiveMode) {
+            mHandler.post(() -> {
+                // Cancel any existing animations on the PIP once the user starts dragging it
+                if (mBoundsAnimator != null && inInteractiveMode) {
+                    mBoundsAnimator.cancel();
+                }
+                mInInteractiveMode = inInteractiveMode;
+                mPreImeShowingBounds.setEmpty();
+            });
+        }
+    }
+
+    /**
+     * Handler for the case where the listener dies.
+     */
+    private class PinnedStackListenerDeathHandler implements IBinder.DeathRecipient {
+
+        @Override
+        public void binderDied() {
+            // Clean up the state if the listener dies
+            mInInteractiveMode = false;
+            mPinnedStackListener = null;
+        }
+    }
+
+    PinnedStackController(WindowManagerService service, DisplayContent displayContent) {
+        mService = service;
+        mDisplayContent = displayContent;
+        mSnapAlgorithm = new PipSnapAlgorithm(service.mContext);
+        mMotionHelper = new PipMotionHelper(BackgroundThread.getHandler());
+        reloadResources();
+    }
+
+    void onConfigurationChanged() {
+        reloadResources();
+    }
+
+    /**
+     * Reloads all the resources for the current configuration.
+     */
+    void reloadResources() {
+        final Resources res = mService.mContext.getResources();
+        final Size defaultSizeDp = Size.parseSize(res.getString(
+                com.android.internal.R.string.config_defaultPictureInPictureSize));
+        final Size screenEdgeInsetsDp = Size.parseSize(res.getString(
+                com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets));
+        mDefaultStackGravity = res.getInteger(
+                com.android.internal.R.integer.config_defaultPictureInPictureGravity);
+        mDisplayContent.getDisplay().getRealMetrics(mTmpMetrics);
+        mDefaultStackSize = new Size(dpToPx(defaultSizeDp.getWidth(), mTmpMetrics),
+                dpToPx(defaultSizeDp.getHeight(), mTmpMetrics));
+        mScreenEdgeInsets = new Point(dpToPx(screenEdgeInsetsDp.getWidth(), mTmpMetrics),
+                dpToPx(screenEdgeInsetsDp.getHeight(), mTmpMetrics));
+    }
+
+    /**
+     * Registers a pinned stack listener.
+     */
+    void registerPinnedStackListener(IPinnedStackListener listener) {
+        try {
+            listener.asBinder().linkToDeath(mPinnedStackListenerDeathHandler, 0);
+            listener.onListenerRegistered(mCallbacks);
+            mPinnedStackListener = listener;
+            notifyBoundsChanged(mIsImeShowing);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to register pinned stack listener", e);
+        }
+    }
+
+    /**
+     * @return the default bounds to show the PIP when there is no active PIP.
+     */
+    Rect getDefaultBounds() {
+        final Display display = mDisplayContent.getDisplay();
+        final Rect insetBounds = new Rect();
+        final Point displaySize = new Point();
+        display.getRealSize(displaySize);
+        mService.getStableInsetsLocked(mDisplayContent.getDisplayId(), mTmpInsets);
+        getInsetBounds(displaySize, mTmpInsets, insetBounds);
+
+        final Rect defaultBounds = new Rect();
+        Gravity.apply(mDefaultStackGravity, mDefaultStackSize.getWidth(),
+                mDefaultStackSize.getHeight(), insetBounds, 0, 0, defaultBounds);
+        return defaultBounds;
+    }
+
+    /**
+     * @return the movement bounds for the given {@param stackBounds} and the current state of the
+     *         controller.
+     */
+    Rect getMovementBounds(Rect stackBounds) {
+        final Display display = mDisplayContent.getDisplay();
+        final Rect movementBounds = new Rect();
+        final Point displaySize = new Point();
+        display.getRealSize(displaySize);
+        mService.getStableInsetsLocked(mDisplayContent.getDisplayId(), mTmpInsets);
+        getInsetBounds(displaySize, mTmpInsets, movementBounds);
+
+        // Adjust the right/bottom to ensure the stack bounds never goes offscreen
+        movementBounds.right = Math.max(movementBounds.left, movementBounds.right -
+                stackBounds.width());
+        movementBounds.bottom = Math.max(movementBounds.top, movementBounds.bottom -
+                stackBounds.height());
+
+        // Adjust the top if the ime is open
+        if (mIsImeShowing) {
+            movementBounds.bottom -= mImeHeight;
+        }
+
+        return movementBounds;
+    }
+
+    /**
+     * @return the PIP bounds given it's bounds pre-rotation, and post-rotation (with as applied
+     * by the display content, which currently transposes the dimensions but keeps each stack in
+     * the same physical space on the device).
+     */
+    Rect getPostRotationBounds(Rect preRotationStackBounds, Rect postRotationStackBounds) {
+        // Keep the pinned stack in the same aspect ratio as in the old orientation, but
+        // move it into the position in the rotated space, and snap to the closest space
+        // in the new orientation.
+        final Rect movementBounds = getMovementBounds(preRotationStackBounds);
+        final int stackWidth = preRotationStackBounds.width();
+        final int stackHeight = preRotationStackBounds.height();
+        final int left = postRotationStackBounds.centerX() - (stackWidth / 2);
+        final int top = postRotationStackBounds.centerY() - (stackHeight / 2);
+        final Rect postRotBounds = new Rect(left, top, left + stackWidth, top + stackHeight);
+        return mSnapAlgorithm.findClosestSnapBounds(movementBounds, postRotBounds);
+    }
+
+    /**
+     * Sets the Ime state and height.
+     */
+    void setAdjustedForIme(boolean adjustedForIme, int imeHeight) {
+        // Return early if there is no state change
+        if (mIsImeShowing == adjustedForIme && mImeHeight == imeHeight) {
+            return;
+        }
+
+        final Rect stackBounds = new Rect();
+        mService.getStackBounds(PINNED_STACK_ID, stackBounds);
+        final Rect prevMovementBounds = getMovementBounds(stackBounds);
+        final boolean wasAdjustedForIme = mIsImeShowing;
+        mIsImeShowing = adjustedForIme;
+        mImeHeight = imeHeight;
+        if (mInInteractiveMode) {
+            // If the user is currently interacting with the PIP and the ime state changes, then
+            // don't adjust the bounds and defer that to after the interaction
+            notifyBoundsChanged(adjustedForIme /* adjustedForIme */);
+        } else {
+            // Otherwise, we can move the PIP to a sane location to ensure that it does not block
+            // the user from interacting with the IME
+            Rect toBounds;
+            if (!wasAdjustedForIme && adjustedForIme) {
+                // If we are showing the IME, then store the previous bounds
+                mPreImeShowingBounds.set(stackBounds);
+                toBounds = adjustBoundsInMovementBounds(stackBounds);
+            } else if (wasAdjustedForIme && !adjustedForIme) {
+                if (!mPreImeShowingBounds.isEmpty()) {
+                    // If we are hiding the IME and the user is not interacting with the PIP, restore
+                    // the previous bounds
+                    toBounds = mPreImeShowingBounds;
+                } else {
+                    if (stackBounds.top == prevMovementBounds.bottom) {
+                        // If the PIP is resting on top of the IME, then adjust it with the hiding
+                        // of the IME
+                        final Rect movementBounds = getMovementBounds(stackBounds);
+                        toBounds = new Rect(stackBounds);
+                        toBounds.offsetTo(toBounds.left, movementBounds.bottom);
+                    } else {
+                        // Otherwise, leave the PIP in place
+                        toBounds = stackBounds;
+                    }
+                }
+            } else {
+                // Otherwise, the IME bounds have changed so we need to adjust the PIP bounds also
+                toBounds = adjustBoundsInMovementBounds(stackBounds);
+            }
+            if (!toBounds.equals(stackBounds)) {
+                if (mBoundsAnimator != null) {
+                    mBoundsAnimator.cancel();
+                }
+                mBoundsAnimator = mMotionHelper.createAnimationToBounds(stackBounds, toBounds);
+                mBoundsAnimator.start();
+            }
+        }
+    }
+
+    /**
+     * @return the adjusted {@param stackBounds} such that they are in the movement bounds.
+     */
+    private Rect adjustBoundsInMovementBounds(Rect stackBounds) {
+        final Rect movementBounds = getMovementBounds(stackBounds);
+        final Rect adjustedBounds = new Rect(stackBounds);
+        adjustedBounds.offset(0, Math.min(0, movementBounds.bottom - stackBounds.top));
+        return adjustedBounds;
+    }
+
+    /**
+     * Sends a broadcast that the PIP movement bounds have changed.
+     */
+    private void notifyBoundsChanged(boolean adjustedForIme) {
+        if (mPinnedStackListener != null) {
+            try {
+                mPinnedStackListener.onBoundsChanged(adjustedForIme);
+            } catch (RemoteException e) {
+                Slog.e(TAG_WM, "Error delivering bounds changed event.", e);
+            }
+        }
+    }
+
+    /**
+     * @return the bounds on the screen that the PIP can be visible in.
+     */
+    private void getInsetBounds(Point displaySize, Rect insets, Rect outRect) {
+        outRect.set(insets.left + mScreenEdgeInsets.x, insets.top + mScreenEdgeInsets.y,
+                displaySize.x - insets.right - mScreenEdgeInsets.x,
+                displaySize.y - insets.bottom - mScreenEdgeInsets.y);
+    }
+
+    /**
+     * @return the pixels for a given dp value.
+     */
+    private int dpToPx(float dpValue, DisplayMetrics dm) {
+        return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm);
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "PinnedStackController");
+        pw.println(prefix + "  mIsImeShowing=" + mIsImeShowing);
+        pw.println(prefix + "  mInInteractiveMode=" + mInInteractiveMode);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 90e27ea..a28dc10 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import android.app.AppOpsManager;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.hardware.power.V1_0.PowerHint;
@@ -34,35 +33,26 @@
 import android.util.SparseIntArray;
 import android.view.Display;
 import android.view.DisplayInfo;
-import android.view.InputChannel;
 import android.view.WindowManager;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.EventLogTags;
-import com.android.server.input.InputWindowHandle;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.LinkedList;
 
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.OP_NONE;
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
-import static android.view.WindowManager.INPUT_CONSUMER_PIP;
-import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
-import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -72,7 +62,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
@@ -89,7 +78,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
 import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
-import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
@@ -104,8 +92,6 @@
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
 
 /** Root {@link WindowContainer} for the device. */
-// TODO: Several methods in here are accessing children of this container's children through various
-// references (WindowList I am looking at you :/). See if we can delegate instead.
 class RootWindowContainer extends WindowContainer<DisplayContent> {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "RootWindowContainer" : TAG_WM;
 
@@ -118,13 +104,6 @@
     private float mButtonBrightness = -1;
     private long mUserActivityTimeout = -1;
     private boolean mUpdateRotation = false;
-    private boolean mObscured = false;
-    private boolean mSyswin = false;
-    // Set to true when the display contains content to show the user.
-    // When false, the display manager may choose to mirror or blank the display.
-    private boolean mDisplayHasContent = false;
-    private float mPreferredRefreshRate = 0;
-    private int mPreferredModeId = 0;
     // Following variables are for debugging screen wakelock only.
     // Last window that requires screen wakelock
     WindowState mHoldScreenWindow = null;
@@ -147,8 +126,6 @@
 
     private final ArrayList<Integer> mChangedStackList = new ArrayList();
 
-    private final LinkedList<AppWindowToken> mTmpUpdateAllDrawn = new LinkedList();
-
     private final ArrayList<WindowToken> mTmpTokensList = new ArrayList();
 
     // Collection of binder tokens mapped to their window type we are allowed to create window
@@ -305,14 +282,14 @@
         final int count = mChildren.size();
         for (int i = 0; i < count; ++i) {
             final DisplayContent dc = mChildren.get(i);
-            output.addAll(dc.getWindowList());
+            dc.getWindows(output);
         }
     }
 
     void getWindows(WindowList output, boolean visibleOnly, boolean appsOnly) {
         final int numDisplays = mChildren.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final WindowList windowList = mChildren.get(displayNdx).getWindowList();
+            final ReadOnlyWindowList windowList = mChildren.get(displayNdx).getReadOnlyWindowList();
             for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) {
                 final WindowState w = windowList.get(winNdx);
                 if ((!visibleOnly || w.mWinAnimator.getShown())
@@ -333,7 +310,7 @@
         }
         final int numDisplays = mChildren.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final WindowList windowList = mChildren.get(displayNdx).getWindowList();
+            final ReadOnlyWindowList windowList = mChildren.get(displayNdx).getReadOnlyWindowList();
             for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) {
                 final WindowState w = windowList.get(winNdx);
                 if (name != null) {
@@ -350,7 +327,7 @@
     WindowState findWindow(int hashCode) {
         final int numDisplays = mChildren.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final WindowList windows = mChildren.get(displayNdx).getWindowList();
+            final ReadOnlyWindowList windows = mChildren.get(displayNdx).getReadOnlyWindowList();
             final int numWindows = windows.size();
             for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
                 final WindowState w = windows.get(winNdx);
@@ -645,7 +622,7 @@
 
     void setSecureSurfaceState(int userId, boolean disabled) {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowList windows = mChildren.get(i).getWindowList();
+            final ReadOnlyWindowList windows = mChildren.get(i).getReadOnlyWindowList();
             for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
                 final WindowState win = windows.get(winNdx);
                 if (win.mHasSurface && userId == UserHandle.getUserId(win.mOwnerUid)) {
@@ -658,17 +635,16 @@
     void updateAppOpsState() {
         final int count = mChildren.size();
         for (int i = 0; i < count; ++i) {
-            final WindowList windows = mChildren.get(i).getWindowList();
+            final ReadOnlyWindowList windows = mChildren.get(i).getReadOnlyWindowList();
             final int numWindows = windows.size();
             for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
                 final WindowState win = windows.get(winNdx);
-                if (win.mAppOp == AppOpsManager.OP_NONE) {
+                if (win.mAppOp == OP_NONE) {
                     continue;
                 }
                 final int mode = mService.mAppOps.checkOpNoThrow(win.mAppOp, win.getOwningUid(),
                         win.getOwningPackage());
-                win.setAppOpVisibilityLw(mode == AppOpsManager.MODE_ALLOWED ||
-                        mode == AppOpsManager.MODE_DEFAULT);
+                win.setAppOpVisibilityLw(mode == MODE_ALLOWED || mode == MODE_DEFAULT);
             }
         }
     }
@@ -676,7 +652,7 @@
     boolean canShowStrictModeViolation(int pid) {
         final int count = mChildren.size();
         for (int i = 0; i < count; ++i) {
-            final WindowList windows = mChildren.get(i).getWindowList();
+            final ReadOnlyWindowList windows = mChildren.get(i).getReadOnlyWindowList();
             final int numWindows = windows.size();
             for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
                 final WindowState ws = windows.get(winNdx);
@@ -691,7 +667,7 @@
     void closeSystemDialogs(String reason) {
         final int count = mChildren.size();
         for (int i = 0; i < count; ++i) {
-            final WindowList windows = mChildren.get(i).getWindowList();
+            final ReadOnlyWindowList windows = mChildren.get(i).getReadOnlyWindowList();
             final int numWindows = windows.size();
             for (int j = 0; j < numWindows; ++j) {
                 final WindowState w = windows.get(j);
@@ -711,7 +687,7 @@
         try {
             for (int i = mChildren.size() - 1; i >= 0; i--) {
                 DisplayContent dc = mChildren.get(i);
-                final WindowList windows = dc.getWindowList();
+                final ReadOnlyWindowList windows = mChildren.get(i).getReadOnlyWindowList();
                 for (int j = windows.size() - 1; j >= 0; j--) {
                     final WindowState win = windows.get(j);
                     final AppWindowToken aToken = win.mAppToken;
@@ -745,84 +721,10 @@
     }
 
     void updateInputWindows(InputMonitor inputMonitor, WindowState inputFocus, boolean inDrag) {
-        final InputConsumerImpl navInputConsumer =
-                mService.mInputMonitor.getInputConsumer(INPUT_CONSUMER_NAVIGATION);
-        final InputConsumerImpl pipInputConsumer =
-                mService.mInputMonitor.getInputConsumer(INPUT_CONSUMER_PIP);
-        final InputConsumerImpl wallpaperInputConsumer =
-                mService.mInputMonitor.getInputConsumer(INPUT_CONSUMER_WALLPAPER);
-        boolean addInputConsumerHandle = navInputConsumer != null;
-        boolean addPipInputConsumerHandle = pipInputConsumer != null;
-        boolean addWallpaperInputConsumerHandle = wallpaperInputConsumer != null;
-        final Rect pipTouchableBounds = addPipInputConsumerHandle ? new Rect() : null;
-        boolean disableWallpaperTouchEvents = false;
-
         final int count = mChildren.size();
         for (int i = 0; i < count; ++i) {
             final DisplayContent dc = mChildren.get(i);
-            final WindowList windows = dc.getWindowList();
-            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                final WindowState child = windows.get(winNdx);
-                final InputChannel inputChannel = child.mInputChannel;
-                final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
-                if (inputChannel == null || inputWindowHandle == null || child.mRemoved
-                        || child.isAdjustedForMinimizedDock()) {
-                    // Skip this window because it cannot possibly receive input.
-                    continue;
-                }
-
-                if (addPipInputConsumerHandle
-                        && child.getStackId() == PINNED_STACK_ID
-                        && inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer) {
-                    // Update the bounds of the Pip input consumer to match the Pinned stack
-                    child.getStack().getBounds(pipTouchableBounds);
-                    pipInputConsumer.mWindowHandle.touchableRegion.set(pipTouchableBounds);
-                    inputMonitor.addInputWindowHandle(pipInputConsumer.mWindowHandle);
-                    addPipInputConsumerHandle = false;
-                }
-
-                if (addInputConsumerHandle
-                        && inputWindowHandle.layer <= navInputConsumer.mWindowHandle.layer) {
-                    inputMonitor.addInputWindowHandle(navInputConsumer.mWindowHandle);
-                    addInputConsumerHandle = false;
-                }
-
-                if (addWallpaperInputConsumerHandle) {
-                    if (child.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER &&
-                            child.isVisibleLw()) {
-                        // Add the wallpaper input consumer above the first visible wallpaper.
-                        inputMonitor.addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
-                        addWallpaperInputConsumerHandle = false;
-                    }
-                }
-
-                final int flags = child.mAttrs.flags;
-                final int privateFlags = child.mAttrs.privateFlags;
-                final int type = child.mAttrs.type;
-
-                final boolean hasFocus = child == inputFocus;
-                final boolean isVisible = child.isVisibleLw();
-                if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) {
-                    disableWallpaperTouchEvents = true;
-                }
-                final boolean hasWallpaper = dc.mWallpaperController.isWallpaperTarget(child)
-                        && (privateFlags & PRIVATE_FLAG_KEYGUARD) == 0
-                        && !disableWallpaperTouchEvents;
-
-                // If there's a drag in progress and 'child' is a potential drop target,
-                // make sure it's been told about the drag
-                if (inDrag && isVisible && dc.isDefaultDisplay) {
-                    mService.mDragState.sendDragStartedIfNeededLw(child);
-                }
-
-                inputMonitor.addInputWindowHandle(
-                        inputWindowHandle, child, flags, type, isVisible, hasFocus, hasWallpaper);
-            }
-        }
-
-        if (addWallpaperInputConsumerHandle) {
-            // No visible wallpaper found, add the wallpaper input consumer at the end.
-            inputMonitor.addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
+            dc.updateInputWindows(inputMonitor, inputFocus, inDrag);
         }
     }
 
@@ -837,57 +739,33 @@
 
         final long callingIdentity = Binder.clearCallingIdentity();
         try {
-            // There was some problem...   first, do a sanity check of the window list to make sure
+            // There was some problem...first, do a sanity check of the window list to make sure
             // we haven't left any dangling surfaces around.
 
             Slog.i(TAG_WM, "Out of memory for surface!  Looking for leaks...");
             final int numDisplays = mChildren.size();
             for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-                final WindowList windows = mChildren.get(displayNdx).getWindowList();
-                final int numWindows = windows.size();
-                for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
-                    final WindowState ws = windows.get(winNdx);
-                    final WindowStateAnimator wsa = ws.mWinAnimator;
-                    if (wsa.mSurfaceController == null) {
-                        continue;
-                    }
-                    if (!mService.mSessions.contains(wsa.mSession)) {
-                        Slog.w(TAG_WM, "LEAKED SURFACE (session doesn't exist): "
-                                + ws + " surface=" + wsa.mSurfaceController
-                                + " token=" + ws.mToken
-                                + " pid=" + ws.mSession.mPid
-                                + " uid=" + ws.mSession.mUid);
-                        wsa.destroySurface();
-                        mService.mForceRemoves.add(ws);
-                        leakedSurface = true;
-                    } else if (ws.mAppToken != null && ws.mAppToken.clientHidden) {
-                        Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): "
-                                + ws + " surface=" + wsa.mSurfaceController
-                                + " token=" + ws.mAppToken
-                                + " saved=" + ws.hasSavedSurface());
-                        if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", false);
-                        wsa.destroySurface();
-                        leakedSurface = true;
-                    }
-                }
+                leakedSurface |= mChildren.get(displayNdx).destroyLeakedSurfaces();
             }
 
             if (!leakedSurface) {
                 Slog.w(TAG_WM, "No leaked surfaces; killing applications!");
-                SparseIntArray pidCandidates = new SparseIntArray();
+                final SparseIntArray pidCandidates = new SparseIntArray();
                 for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-                    final WindowList windows = mChildren.get(displayNdx).getWindowList();
+                    final ReadOnlyWindowList windows =
+                            mChildren.get(displayNdx).getReadOnlyWindowList();
                     final int numWindows = windows.size();
                     for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
                         final WindowState ws = windows.get(winNdx);
                         if (mService.mForceRemoves.contains(ws)) {
                             continue;
                         }
-                        WindowStateAnimator wsa = ws.mWinAnimator;
+                        final WindowStateAnimator wsa = ws.mWinAnimator;
                         if (wsa.mSurfaceController != null) {
                             pidCandidates.append(wsa.mSession.mPid, wsa.mSession.mPid);
                         }
                     }
+
                     if (pidCandidates.size() > 0) {
                         int[] pids = new int[pidCandidates.size()];
                         for (int i = 0; i < pids.length; i++) {
@@ -1238,8 +1116,8 @@
                 "performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating());
     }
 
-    // TODO: Super crazy long method that should be broken down...
-    private void applySurfaceChangesTransaction(boolean recoveringMemory, int defaultDw, int defaultDh) {
+    private void applySurfaceChangesTransaction(boolean recoveringMemory, int defaultDw,
+            int defaultDh) {
         mHoldScreenWindow = null;
         mObscuringWindow = null;
 
@@ -1262,214 +1140,31 @@
         final int count = mChildren.size();
         for (int j = 0; j < count; ++j) {
             final DisplayContent dc = mChildren.get(j);
-            WindowList windows = dc.getWindowList();
-            DisplayInfo displayInfo = dc.getDisplayInfo();
-            final int displayId = dc.getDisplayId();
-            final int dw = displayInfo.logicalWidth;
-            final int dh = displayInfo.logicalHeight;
-            final boolean isDefaultDisplay = (displayId == DEFAULT_DISPLAY);
-            final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
-
-            // Reset for each display.
-            mDisplayHasContent = false;
-            mPreferredRefreshRate = 0;
-            mPreferredModeId = 0;
-            mTmpUpdateAllDrawn.clear();
-
-            int repeats = 0;
-            do {
-                repeats++;
-                if (repeats > 6) {
-                    Slog.w(TAG, "Animation repeat aborted after too many iterations");
-                    dc.clearLayoutNeeded();
-                    break;
-                }
-
-                if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
-                        "On entry to LockedInner", dc.pendingLayoutChanges);
-
-                if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
-                    dc.adjustWallpaperWindows();
-                }
-
-                if (isDefaultDisplay
-                        && (dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
-                    if (mService.updateOrientationFromAppTokensLocked(true, displayId)) {
-                        dc.setLayoutNeeded();
-                        mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
-                    }
-                }
-
-                if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
-                    dc.setLayoutNeeded();
-                }
-
-                // FIRST LOOP: Perform a layout, if needed.
-                if (repeats < LAYOUT_REPEAT_THRESHOLD) {
-                    surfacePlacer.performLayoutLockedInner(dc, repeats == 1,
-                            false /* updateInputWindows */);
-                } else {
-                    Slog.w(TAG, "Layout repeat skipped after too many iterations");
-                }
-
-                // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
-                // it is animating.
-                dc.pendingLayoutChanges = 0;
-
-                if (isDefaultDisplay) {
-                    mService.mPolicy.beginPostLayoutPolicyLw(dw, dh);
-                    for (int i = windows.size() - 1; i >= 0; i--) {
-                        WindowState w = windows.get(i);
-                        if (w.mHasSurface) {
-                            mService.mPolicy.applyPostLayoutPolicyLw(
-                                    w, w.mAttrs, w.getParentWindow());
-                        }
-                    }
-                    dc.pendingLayoutChanges |=
-                            mService.mPolicy.finishPostLayoutPolicyLw();
-                    if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
-                            "after finishPostLayoutPolicyLw", dc.pendingLayoutChanges);
-                }
-            } while (dc.pendingLayoutChanges != 0);
-
-            mObscured = false;
-            mSyswin = false;
-            dc.resetDimming();
-
-            // Only used if default window
-            final boolean someoneLosingFocus = !mService.mLosingFocus.isEmpty();
-
-            for (int i = windows.size() - 1; i >= 0; i--) {
-                WindowState w = windows.get(i);
-                final Task task = w.getTask();
-                final boolean obscuredChanged = w.mObscured != mObscured;
-
-                // Update effect.
-                w.mObscured = mObscured;
-                if (!mObscured) {
-                    handleNotObscuredLocked(w, displayInfo);
-                }
-
-                w.applyDimLayerIfNeeded();
-
-                if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
-                        && dc.mWallpaperController.isWallpaperTarget(w)) {
-                    // This is the wallpaper target and its obscured state changed... make sure the
-                    // current wallpaper's visibility has been updated accordingly.
-                    dc.mWallpaperController.updateWallpaperVisibility();
-                }
-
-                w.handleWindowMovedIfNeeded();
-
-                final WindowStateAnimator winAnimator = w.mWinAnimator;
-
-                //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
-                w.mContentChanged = false;
-
-                // Moved from updateWindowsAndWallpaperLocked().
-                if (w.mHasSurface) {
-                    // Take care of the window being ready to display.
-                    final boolean committed = winAnimator.commitFinishDrawingLocked();
-                    if (isDefaultDisplay && committed) {
-                        if (w.mAttrs.type == TYPE_DREAM) {
-                            // HACK: When a dream is shown, it may at that point hide the lock
-                            // screen. So we need to redo the layout to let the phone window manager
-                            // make this happen.
-                            dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
-                            if (DEBUG_LAYOUT_REPEATS) {
-                                surfacePlacer.debugLayoutRepeats(
-                                        "dream and commitFinishDrawingLocked true",
-                                        dc.pendingLayoutChanges);
-                            }
-                        }
-                        if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
-                            if (DEBUG_WALLPAPER_LIGHT)
-                                Slog.v(TAG, "First draw done in potential wallpaper target " + w);
-                            mWallpaperMayChange = true;
-                            dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
-                            if (DEBUG_LAYOUT_REPEATS) {
-                                surfacePlacer.debugLayoutRepeats(
-                                        "wallpaper and commitFinishDrawingLocked true",
-                                        dc.pendingLayoutChanges);
-                            }
-                        }
-                    }
-                    if (!winAnimator.isAnimationStarting() && !winAnimator.isWaitingForOpening()) {
-                        // Updates the shown frame before we set up the surface. This is needed
-                        // because the resizing could change the top-left position (in addition to
-                        // size) of the window. setSurfaceBoundariesLocked uses mShownPosition to
-                        // position the surface.
-                        //
-                        // If an animation is being started, we can't call this method because the
-                        // animation hasn't processed its initial transformation yet, but in general
-                        // we do want to update the position if the window is animating.
-                        winAnimator.computeShownFrameLocked();
-                    }
-                    winAnimator.setSurfaceBoundariesLocked(recoveringMemory);
-                }
-
-                final AppWindowToken atoken = w.mAppToken;
-                if (atoken != null) {
-                    final boolean updateAllDrawn = atoken.updateDrawnWindowStates(w);
-                    if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(atoken)) {
-                        mTmpUpdateAllDrawn.add(atoken);
-                    }
-                }
-
-                if (isDefaultDisplay && someoneLosingFocus && w == mService.mCurrentFocus
-                        && w.isDisplayedLw()) {
-                    focusDisplayed = true;
-                }
-
-                w.updateResizingWindowIfNeeded();
-            }
-
-            mService.mDisplayManagerInternal.setDisplayProperties(displayId,
-                    mDisplayHasContent,
-                    mPreferredRefreshRate,
-                    mPreferredModeId,
-                    true /* inTraversal, must call performTraversalInTrans... below */);
-
-            dc.stopDimmingIfNeeded();
-
-            while (!mTmpUpdateAllDrawn.isEmpty()) {
-                final AppWindowToken atoken = mTmpUpdateAllDrawn.removeLast();
-                // See if any windows have been drawn, so they (and others associated with them)
-                // can now be shown.
-                atoken.updateAllDrawn(dc);
-            }
+            focusDisplayed |= dc.applySurfaceChangesTransaction(recoveringMemory);
         }
 
         if (focusDisplayed) {
             mService.mH.sendEmptyMessage(REPORT_LOSING_FOCUS);
         }
 
-        // Give the display manager a chance to adjust properties
-        // like display rotation if it needs to.
+        // Give the display manager a chance to adjust properties like display rotation if it needs
+        // to.
         mService.mDisplayManagerInternal.performTraversalInTransactionFromWindowManager();
     }
 
     /**
      * @param w WindowState this method is applied to.
-     * @param dispInfo info of the display that the window's obscuring state is checked against.
+     * @param obscured True if there is a window on top of this obscuring the display.
+     * @param syswin System window?
+     * @return True when the display contains content to show the user. When false, the display
+     *          manager may choose to mirror or blank the display.
      */
-    private void handleNotObscuredLocked(final WindowState w, final DisplayInfo dispInfo) {
+    boolean handleNotObscuredLocked(WindowState w, boolean obscured, boolean syswin) {
         final WindowManager.LayoutParams attrs = w.mAttrs;
         final int attrFlags = attrs.flags;
         final boolean canBeSeen = w.isDisplayedLw();
         final int privateflags = attrs.privateFlags;
-
-        if (canBeSeen && w.isObscuringFullscreen(dispInfo)) {
-            // This window completely covers everything behind it,
-            // so we want to leave all of them as undimmed (for
-            // performance reasons).
-            if (!mObscured) {
-                mObscuringWindow = w;
-            }
-
-            mObscured = true;
-        }
+        boolean displayHasContent = false;
 
         if (w.mHasSurface && canBeSeen) {
             if ((attrFlags & FLAG_KEEP_SCREEN_ON) != 0) {
@@ -1480,22 +1175,17 @@
                         + "screen wakelock but no longer has FLAG_KEEP_SCREEN_ON!!! called by"
                         + Debug.getCallers(10));
             }
-            if (!mSyswin && w.mAttrs.screenBrightness >= 0 && mScreenBrightness < 0) {
+            if (!syswin && w.mAttrs.screenBrightness >= 0 && mScreenBrightness < 0) {
                 mScreenBrightness = w.mAttrs.screenBrightness;
             }
-            if (!mSyswin && w.mAttrs.buttonBrightness >= 0 && mButtonBrightness < 0) {
+            if (!syswin && w.mAttrs.buttonBrightness >= 0 && mButtonBrightness < 0) {
                 mButtonBrightness = w.mAttrs.buttonBrightness;
             }
-            if (!mSyswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
+            if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
                 mUserActivityTimeout = w.mAttrs.userActivityTimeout;
             }
 
             final int type = attrs.type;
-            if (type == TYPE_SYSTEM_DIALOG || type == TYPE_SYSTEM_ERROR
-                    || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
-                mSyswin = true;
-            }
-
             // This function assumes that the contents of the default display are processed first
             // before secondary displays.
             final DisplayContent displayContent = w.getDisplayContent();
@@ -1506,23 +1196,19 @@
                 if (type == TYPE_DREAM || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
                     mObscureApplicationContentOnSecondaryDisplays = true;
                 }
-                mDisplayHasContent = true;
+                displayHasContent = true;
             } else if (displayContent != null &&
                     (!mObscureApplicationContentOnSecondaryDisplays
-                            || (mObscured && type == TYPE_KEYGUARD_DIALOG))) {
+                            || (obscured && type == TYPE_KEYGUARD_DIALOG))) {
                 // Allow full screen keyguard presentation dialogs to be seen.
-                mDisplayHasContent = true;
-            }
-            if (mPreferredRefreshRate == 0 && w.mAttrs.preferredRefreshRate != 0) {
-                mPreferredRefreshRate = w.mAttrs.preferredRefreshRate;
-            }
-            if (mPreferredModeId == 0 && w.mAttrs.preferredDisplayModeId != 0) {
-                mPreferredModeId = w.mAttrs.preferredDisplayModeId;
+                displayHasContent = true;
             }
             if ((privateflags & PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE) != 0) {
                 mSustainedPerformanceModeCurrent = true;
             }
         }
+
+        return displayHasContent;
     }
 
     boolean copyAnimToLayoutParams() {
@@ -1619,7 +1305,7 @@
     void dumpWindowsNoHeader(PrintWriter pw, boolean dumpAll, ArrayList<WindowState> windows) {
         final int numDisplays = mChildren.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final WindowList windowList = mChildren.get(displayNdx).getWindowList();
+            final ReadOnlyWindowList windowList = mChildren.get(displayNdx).getReadOnlyWindowList();
             for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) {
                 final WindowState w = windowList.get(winNdx);
                 if (windows == null || windows.contains(w)) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 5637e51..4d8f29d 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -387,24 +387,8 @@
         mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
         switch (mStackId) {
             case PINNED_STACK_ID:
-                // Keep the pinned stack in the same aspect ratio as in the old orientation, but
-                // move it into the position in the rotated space, and snap to the closest space
-                // in the new orientation.
-
-                try {
-                    final IActivityManager am = mService.mActivityManager;
-                    final Rect movementBounds = am.getPictureInPictureMovementBounds(
-                            mDisplayContent.getDisplayId());
-                    final int width = mBounds.width();
-                    final int height = mBounds.height();
-                    final int left = mTmpRect2.centerX() - (width / 2);
-                    final int top = mTmpRect2.centerY() - (height / 2);
-                    mTmpRect2.set(left, top, left + width, top + height);
-
-                    final PipSnapAlgorithm snapAlgorithm = new PipSnapAlgorithm(mService.mContext,
-                            mDisplayContent.getDisplayId());
-                    mTmpRect2.set(snapAlgorithm.findClosestSnapBounds(movementBounds, mTmpRect2));
-                } catch (RemoteException e) {}
+                mTmpRect2 = mDisplayContent.getPinnedStackController().getPostRotationBounds(
+                        mBounds, mTmpRect2);
                 break;
             case DOCKED_STACK_ID:
                 repositionDockedStackAfterRotation(mTmpRect2);
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 4ab8887..c9f1ffc 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -374,7 +374,7 @@
         return mWallpaperAnimLayerAdjustment;
     }
 
-    private void findWallpaperTarget(WindowList windows, FindWallpaperTargetResult result) {
+    private void findWallpaperTarget(ReadOnlyWindowList windows, FindWallpaperTargetResult result) {
         final WindowAnimator winAnimator = mService.mAnimator;
         result.reset();
         WindowState w = null;
@@ -457,7 +457,7 @@
 
     /** Updates the target wallpaper if needed and returns true if an update happened. */
     private boolean updateWallpaperWindowsTarget(
-            WindowList windows, FindWallpaperTargetResult result) {
+            ReadOnlyWindowList windows, FindWallpaperTargetResult result) {
 
         WindowState wallpaperTarget = result.wallpaperTarget;
         int wallpaperTargetIndex = result.wallpaperTargetIndex;
@@ -558,7 +558,7 @@
         return true;
     }
 
-    private boolean updateWallpaperWindowsTargetByLayer(WindowList windows,
+    private boolean updateWallpaperWindowsTargetByLayer(ReadOnlyWindowList windows,
             FindWallpaperTargetResult result) {
 
         WindowState wallpaperTarget = result.wallpaperTarget;
@@ -609,7 +609,7 @@
         return visible;
     }
 
-    private boolean updateWallpaperWindowsPlacement(WindowList windows,
+    private boolean updateWallpaperWindowsPlacement(ReadOnlyWindowList windows,
             WindowState wallpaperTarget, int wallpaperTargetIndex, boolean visible) {
 
         // TODO(multidisplay): Wallpapers on main screen only.
@@ -628,7 +628,7 @@
         return changed;
     }
 
-    boolean adjustWallpaperWindows(WindowList windows) {
+    boolean adjustWallpaperWindows(ReadOnlyWindowList windows) {
         mService.mRoot.mWallpaperMayChange = false;
 
         // First find top-most window that has asked to be on top of the wallpaper;
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index e184e39..d94094a 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -19,7 +19,6 @@
 import android.util.Slog;
 import android.view.Display;
 
-import java.io.PrintWriter;
 import java.util.ArrayDeque;
 
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
@@ -27,7 +26,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
 import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
 
 /**
@@ -63,7 +61,7 @@
     private WindowState mDockDivider = null;
     private ArrayDeque<WindowState> mReplacingWindows = new ArrayDeque<>();
 
-    final void assignWindowLayers(WindowList windows) {
+    final void assignWindowLayers(ReadOnlyWindowList windows) {
         if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based on windows=" + windows,
                 new RuntimeException("here").fillInStackTrace());
 
@@ -113,7 +111,7 @@
         if (DEBUG_LAYERS) logDebugLayers(windows);
     }
 
-    private void logDebugLayers(WindowList windows) {
+    private void logDebugLayers(ReadOnlyWindowList windows) {
         for (int i = 0, n = windows.size(); i < n; i++) {
             final WindowState w = windows.get(i);
             final WindowStateAnimator winAnimator = w.mWinAnimator;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1b08f16..7759705 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -97,6 +97,7 @@
 import android.view.IDockedStackListener;
 import android.view.IInputFilter;
 import android.view.IOnKeyguardExitResult;
+import android.view.IPinnedStackListener;
 import android.view.IRotationWatcher;
 import android.view.IWindow;
 import android.view.IWindowId;
@@ -142,7 +143,6 @@
 import com.android.server.UiThread;
 import com.android.server.Watchdog;
 import com.android.server.input.InputManagerService;
-import com.android.server.policy.PhoneWindowManager;
 import com.android.server.power.ShutdownThread;
 
 import java.io.BufferedWriter;
@@ -166,12 +166,13 @@
 import java.util.List;
 
 import static android.Manifest.permission.MANAGE_APP_TOKENS;
+import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.StatusBarManager.DISABLE_MASK;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -186,12 +187,9 @@
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_DRAG;
-import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
@@ -204,6 +202,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.EventLogTags.WM_TASK_CREATED;
 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
@@ -219,7 +218,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEYGUARD;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
@@ -241,7 +239,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
@@ -529,6 +526,7 @@
     private final SparseIntArray mTmpTaskIds = new SparseIntArray();
 
     boolean mForceResizableTasks = false;
+    boolean mSupportsPictureInPicture = false;
 
     int getDragLayerLocked() {
         return mPolicy.windowTypeToLayerLw(TYPE_DRAG) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
@@ -620,10 +618,6 @@
     // window list before it is used whenever window container order changes.
     final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<>();
 
-    /** Temporary list for comparison. Always clear this after use so we don't end up with
-     * orphaned windows references */
-    final ArrayList<WindowState> mTmpWindows = new ArrayList<>();
-
     boolean mHardKeyboardAvailable;
     WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
     SettingsObserver mSettingsObserver;
@@ -3274,51 +3268,6 @@
         mH.sendMessage(m);
     }
 
-    /** Rebuilds the input display's window list and does a relayout if something changed. */
-    private void rebuildAppWindowsAndLayoutIfNeededLocked(DisplayContent displayContent) {
-        final WindowList windows = displayContent.getWindowList();
-        mTmpWindows.addAll(windows);
-
-        displayContent.rebuildAppWindowList();
-
-        // Set displayContent.mLayoutNeeded if window order changed.
-        final int tmpSize = mTmpWindows.size();
-        final int winSize = windows.size();
-        int tmpNdx = 0, winNdx = 0;
-        while (tmpNdx < tmpSize && winNdx < winSize) {
-            // Skip over all exiting windows, they've been moved out of order.
-            WindowState tmp;
-            do {
-                tmp = mTmpWindows.get(tmpNdx++);
-            } while (tmpNdx < tmpSize && tmp.mAppToken != null && tmp.mAppToken.mIsExiting);
-
-            WindowState win;
-            do {
-                win = windows.get(winNdx++);
-            } while (winNdx < winSize && win.mAppToken != null && win.mAppToken.mIsExiting);
-
-            if (tmp != win) {
-                // Window order changed.
-                displayContent.setLayoutNeeded();
-                break;
-            }
-        }
-        if (tmpNdx != winNdx) {
-            // One list was different from the other.
-            displayContent.setLayoutNeeded();
-        }
-        mTmpWindows.clear();
-
-        if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
-                false /*updateInputWindows*/)) {
-            displayContent.assignWindowLayers(false /* setLayoutNeeded */);
-        }
-
-        mInputMonitor.setUpdateInputWindowsNeededLw();
-        mWindowPlacerLocked.performSurfacePlacement();
-        mInputMonitor.updateInputWindowsLw(false /*force*/);
-    }
-
     public void moveTaskToTop(int taskId) {
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -3343,7 +3292,7 @@
                 if (mAppTransition.isTransitionSet()) {
                     task.setSendingToBottom(false);
                 }
-                rebuildAppWindowsAndLayoutIfNeededLocked(displayContent);
+                displayContent.rebuildAppWindowsAndLayoutIfNeeded();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -3365,7 +3314,7 @@
                 if (mAppTransition.isTransitionSet()) {
                     task.setSendingToBottom(true);
                 }
-                rebuildAppWindowsAndLayoutIfNeededLocked(stack.getDisplayContent());
+                stack.getDisplayContent().rebuildAppWindowsAndLayoutIfNeeded();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -3388,6 +3337,36 @@
         mDockedStackCreateBounds = bounds;
     }
 
+    @Override
+    public Rect getPictureInPictureDefaultBounds(int displayId) {
+        synchronized (mWindowMap) {
+            if (!mSupportsPictureInPicture) {
+                return new Rect();
+            }
+
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            return displayContent.getPinnedStackController().getDefaultBounds();
+        }
+    }
+
+    @Override
+    public Rect getPictureInPictureMovementBounds(int displayId) {
+        synchronized (mWindowMap) {
+            if (!mSupportsPictureInPicture) {
+                return new Rect();
+            }
+
+            final Rect stackBounds = new Rect();
+            getStackBounds(PINNED_STACK_ID, stackBounds);
+            if (stackBounds.isEmpty()) {
+                return stackBounds;
+            }
+
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            return displayContent.getPinnedStackController().getMovementBounds(stackBounds);
+        }
+    }
+
     /**
      * Create a new TaskStack and place it on a DisplayContent.
      * @param stackId The unique identifier of the new stack.
@@ -4400,29 +4379,6 @@
         SystemProperties.set(StrictMode.VISUAL_PROPERTY, value);
     }
 
-    private static void convertCropForSurfaceFlinger(Rect crop, int rot, int dw, int dh) {
-        if (rot == Surface.ROTATION_90) {
-            final int tmp = crop.top;
-            crop.top = dw - crop.right;
-            crop.right = crop.bottom;
-            crop.bottom = dw - crop.left;
-            crop.left = tmp;
-        } else if (rot == Surface.ROTATION_180) {
-            int tmp = crop.top;
-            crop.top = dh - crop.bottom;
-            crop.bottom = dh - tmp;
-            tmp = crop.right;
-            crop.right = dw - crop.left;
-            crop.left = dw - tmp;
-        } else if (rot == Surface.ROTATION_270) {
-            final int tmp = crop.top;
-            crop.top = crop.left;
-            crop.left = dh - crop.bottom;
-            crop.bottom = crop.right;
-            crop.right = dh - tmp;
-        }
-    }
-
     @Override
     public Bitmap screenshotWallpaper() {
         if (!checkCallingPermission(Manifest.permission.READ_FRAME_BUFFER,
@@ -4501,8 +4457,8 @@
      * @param config of the output bitmap
      * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
      */
-    Bitmap screenshotApplicationsInner(IBinder appToken, int displayId, int width, int height,
-            boolean includeFullDisplay, float frameScale, Bitmap.Config config,
+    private Bitmap screenshotApplicationsInner(IBinder appToken, int displayId, int width,
+            int height, boolean includeFullDisplay, float frameScale, Bitmap.Config config,
             boolean wallpaperOnly) {
         final DisplayContent displayContent;
         synchronized(mWindowMap) {
@@ -4513,263 +4469,8 @@
                 return null;
             }
         }
-        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-        int dw = displayInfo.logicalWidth;
-        int dh = displayInfo.logicalHeight;
-        if (dw == 0 || dh == 0) {
-            if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
-                    + ": returning null. logical widthxheight=" + dw + "x" + dh);
-            return null;
-        }
-
-        Bitmap bm = null;
-
-        int maxLayer = 0;
-        final Rect frame = new Rect();
-        final Rect stackBounds = new Rect();
-
-        boolean screenshotReady;
-        int minLayer;
-        if (appToken == null && !wallpaperOnly) {
-            screenshotReady = true;
-            minLayer = 0;
-        } else {
-            screenshotReady = false;
-            minLayer = Integer.MAX_VALUE;
-        }
-
-        WindowState appWin = null;
-
-        boolean includeImeInScreenshot;
-        synchronized(mWindowMap) {
-            final AppWindowToken imeTargetAppToken =
-                    mInputMethodTarget != null ? mInputMethodTarget.mAppToken : null;
-            // We only include the Ime in the screenshot if the app we are screenshoting is the IME
-            // target and isn't in multi-window mode. We don't screenshot the IME in multi-window
-            // mode because the frame of the IME might not overlap with that of the app.
-            // E.g. IME target app at the top in split-screen mode and the IME at the bottom
-            // overlapping with the bottom app.
-            includeImeInScreenshot = imeTargetAppToken != null
-                    && imeTargetAppToken.appToken != null
-                    && imeTargetAppToken.appToken.asBinder() == appToken
-                    && !mInputMethodTarget.isInMultiWindowMode();
-        }
-
-        final int aboveAppLayer = (mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1)
-                * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
-
-        synchronized(mWindowMap) {
-            // Figure out the part of the screen that is actually the app.
-            appWin = null;
-            final WindowList windows = displayContent.getWindowList();
-            for (int i = windows.size() - 1; i >= 0; i--) {
-                WindowState ws = windows.get(i);
-                if (!ws.mHasSurface) {
-                    continue;
-                }
-                if (ws.mLayer >= aboveAppLayer) {
-                    continue;
-                }
-                if (wallpaperOnly && !ws.mIsWallpaper) {
-                    continue;
-                }
-                if (ws.mIsImWindow) {
-                    if (!includeImeInScreenshot) {
-                        continue;
-                    }
-                } else if (ws.mIsWallpaper) {
-                    // If this is the wallpaper layer and we're only looking for the wallpaper layer
-                    // then the target window state is this one.
-                    if (wallpaperOnly) {
-                        appWin = ws;
-                    }
-
-                    if (appWin == null) {
-                        // We have not ran across the target window yet, so it is probably
-                        // behind the wallpaper. This can happen when the keyguard is up and
-                        // all windows are moved behind the wallpaper. We don't want to
-                        // include the wallpaper layer in the screenshot as it will coverup
-                        // the layer of the target window.
-                        continue;
-                    }
-                    // Fall through. The target window is in front of the wallpaper. For this
-                    // case we want to include the wallpaper layer in the screenshot because
-                    // the target window might have some transparent areas.
-                } else if (appToken != null) {
-                    if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
-                        // This app window is of no interest if it is not associated with the
-                        // screenshot app.
-                        continue;
-                    }
-                    appWin = ws;
-                }
-
-                // Include this window.
-
-                final WindowStateAnimator winAnim = ws.mWinAnimator;
-                int layer = winAnim.mSurfaceController.getLayer();
-                if (maxLayer < layer) {
-                    maxLayer = layer;
-                }
-                if (minLayer > layer) {
-                    minLayer = layer;
-                }
-
-                // Don't include wallpaper in bounds calculation
-                if (!includeFullDisplay && !ws.mIsWallpaper) {
-                    final Rect wf = ws.mFrame;
-                    final Rect cr = ws.mContentInsets;
-                    int left = wf.left + cr.left;
-                    int top = wf.top + cr.top;
-                    int right = wf.right - cr.right;
-                    int bottom = wf.bottom - cr.bottom;
-                    frame.union(left, top, right, bottom);
-                    ws.getVisibleBounds(stackBounds);
-                    if (!Rect.intersects(frame, stackBounds)) {
-                        // Set frame empty if there's no intersection.
-                        frame.setEmpty();
-                    }
-                }
-
-                final boolean foundTargetWs =
-                        (ws.mAppToken != null && ws.mAppToken.token == appToken)
-                        || (appWin != null && wallpaperOnly);
-                if (foundTargetWs && ws.isDisplayedLw() && winAnim.getShown()) {
-                    screenshotReady = true;
-                }
-
-                if (ws.isObscuringFullscreen(displayInfo)){
-                    break;
-                }
-            }
-
-            if (appToken != null && appWin == null) {
-                // Can't find a window to snapshot.
-                if (DEBUG_SCREENSHOT) Slog.i(TAG_WM,
-                        "Screenshot: Couldn't find a surface matching " + appToken);
-                return null;
-            }
-
-            if (!screenshotReady) {
-                Slog.i(TAG_WM, "Failed to capture screenshot of " + appToken +
-                        " appWin=" + (appWin == null ? "null" : (appWin + " drawState=" +
-                        appWin.mWinAnimator.mDrawState)));
-                return null;
-            }
-
-            // Screenshot is ready to be taken. Everything from here below will continue
-            // through the bottom of the loop and return a value. We only stay in the loop
-            // because we don't want to release the mWindowMap lock until the screenshot is
-            // taken.
-
-            if (maxLayer == 0) {
-                if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
-                        + ": returning null maxLayer=" + maxLayer);
-                return null;
-            }
-
-            if (!includeFullDisplay) {
-                // Constrain frame to the screen size.
-                if (!frame.intersect(0, 0, dw, dh)) {
-                    frame.setEmpty();
-                }
-            } else {
-                // Caller just wants entire display.
-                frame.set(0, 0, dw, dh);
-            }
-            if (frame.isEmpty()) {
-                return null;
-            }
-
-            if (width < 0) {
-                width = (int) (frame.width() * frameScale);
-            }
-            if (height < 0) {
-                height = (int) (frame.height() * frameScale);
-            }
-
-            // Tell surface flinger what part of the image to crop. Take the top
-            // right part of the application, and crop the larger dimension to fit.
-            Rect crop = new Rect(frame);
-            if (width / (float) frame.width() < height / (float) frame.height()) {
-                int cropWidth = (int)((float)width / (float)height * frame.height());
-                crop.right = crop.left + cropWidth;
-            } else {
-                int cropHeight = (int)((float)height / (float)width * frame.width());
-                crop.bottom = crop.top + cropHeight;
-            }
-
-            // The screenshot API does not apply the current screen rotation.
-            int rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
-
-            if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
-                rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
-            }
-
-            // Surfaceflinger is not aware of orientation, so convert our logical
-            // crop to surfaceflinger's portrait orientation.
-            convertCropForSurfaceFlinger(crop, rot, dw, dh);
-
-            if (DEBUG_SCREENSHOT) {
-                Slog.i(TAG_WM, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
-                        + maxLayer + " appToken=" + appToken);
-                for (int i = 0; i < windows.size(); i++) {
-                    WindowState win = windows.get(i);
-                    WindowSurfaceController controller = win.mWinAnimator.mSurfaceController;
-                    Slog.i(TAG_WM, win + ": " + win.mLayer
-                            + " animLayer=" + win.mWinAnimator.mAnimLayer
-                            + " surfaceLayer=" + ((controller == null)
-                                ? "null" : controller.getLayer()));
-                }
-            }
-
-            ScreenRotationAnimation screenRotationAnimation =
-                    mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
-            final boolean inRotation = screenRotationAnimation != null &&
-                    screenRotationAnimation.isAnimating();
-            if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM,
-                    "Taking screenshot while rotating");
-
-            // We force pending transactions to flush before taking
-            // the screenshot by pushing an empty synchronous transaction.
-            SurfaceControl.openTransaction();
-            SurfaceControl.closeTransactionSync();
-
-            bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer,
-                    inRotation, rot);
-            if (bm == null) {
-                Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
-                        + ") to layer " + maxLayer);
-                return null;
-            }
-        }
-
-        if (DEBUG_SCREENSHOT) {
-            // TEST IF IT's ALL BLACK
-            int[] buffer = new int[bm.getWidth() * bm.getHeight()];
-            bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
-            boolean allBlack = true;
-            final int firstColor = buffer[0];
-            for (int i = 0; i < buffer.length; i++) {
-                if (buffer[i] != firstColor) {
-                    allBlack = false;
-                    break;
-                }
-            }
-            if (allBlack) {
-                Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" +
-                        Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +
-                        (appWin != null ?
-                                appWin.mWinAnimator.mSurfaceController.getLayer() : "null") +
-                        " minLayer=" + minLayer + " maxLayer=" + maxLayer);
-            }
-        }
-
-        // Create a copy of the screenshot that is immutable and backed in ashmem.
-        // This greatly reduces the overhead of passing the bitmap between processes.
-        Bitmap ret = bm.createAshmemBitmap(config);
-        bm.recycle();
-        return ret;
+        return displayContent.screenshotApplications(appToken, displayId, width, height,
+                includeFullDisplay, frameScale, config, wallpaperOnly);
     }
 
     /**
@@ -4895,6 +4596,8 @@
         }
     }
 
+
+    // TODO(multidisplay): Rotate any display? Move to DisplayContent
     /**
      * Updates the current rotation of the specified display.
      *
@@ -4922,7 +4625,8 @@
             // Even if the screen rotation animation has finished (e.g. isAnimating
             // returns false), there is still some time where we haven't yet unfrozen
             // the display. We also need to abort rotation here.
-            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, still finishing previous rotation");
+            if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+                    "Deferring rotation, still finishing previous rotation");
             return false;
         }
 
@@ -4933,7 +4637,7 @@
         }
 
         final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-        final WindowList windows = displayContent.getWindowList();
+        final ReadOnlyWindowList windows = displayContent.getReadOnlyWindowList();
 
         final int oldRotation = mRotation;
         int rotation = mPolicy.rotationForOrientationLw(mLastOrientation, mRotation);
@@ -4973,24 +4677,18 @@
         boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
                 mLastOrientation, rotation);
 
-        if (DEBUG_ORIENTATION) {
-            Slog.v(TAG_WM, "Selected orientation "
-                    + mLastOrientation + ", got rotation " + rotation
-                    + " which has " + (altOrientation ? "incompatible" : "compatible")
-                    + " metrics");
-        }
+        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Selected orientation " + mLastOrientation
+                + ", got rotation " + rotation + " which has "
+                + (altOrientation ? "incompatible" : "compatible") + " metrics");
 
         if (mRotation == rotation && mAltOrientation == altOrientation) {
             // No change.
             return false;
         }
 
-        if (DEBUG_ORIENTATION) {
-            Slog.v(TAG_WM,
-                "Rotation changed to " + rotation + (altOrientation ? " (alt)" : "")
-                + " from " + mRotation + (mAltOrientation ? " (alt)" : "")
-                + ", lastOrientation=" + mLastOrientation);
-        }
+        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Rotation changed to " + rotation
+                + (altOrientation ? " (alt)" : "") + " from " + mRotation
+                + (mAltOrientation ? " (alt)" : "") + ", lastOrientation=" + mLastOrientation);
 
         mRotation = rotation;
         mAltOrientation = altOrientation;
@@ -5082,6 +4780,7 @@
                 w.mLastFreezeDuration = 0;
             }
         }
+
         if (rotateSeamlessly) {
             mH.removeMessages(H.SEAMLESS_ROTATION_TIMEOUT);
             mH.sendEmptyMessageDelayed(H.SEAMLESS_ROTATION_TIMEOUT, SEAMLESS_ROTATION_TIMEOUT_DURATION);
@@ -6943,20 +6642,8 @@
                     // Rotation only supported on primary display.
                     // TODO(multi-display)
                     synchronized(mWindowMap) {
-                        final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                        final WindowList windows = displayContent.getWindowList();
-                        boolean layoutNeeded = false;
-                        for (int i = windows.size() - 1; i >= 0; i--) {
-                            WindowState w = windows.get(i);
-                            if (w.mSeamlesslyRotated) {
-                                layoutNeeded = true;
-                                w.setDisplayLayoutNeeded();
-                                markForSeamlessRotation(w, false);
-                            }
-                        }
-                        if (layoutNeeded) {
-                            mWindowPlacerLocked.performSurfacePlacement();
-                        }
+                        final DisplayContent dc = getDefaultDisplayContentLocked();
+                        dc.onSeamlessRotationTimeout();
                     }
                 }
                 break;
@@ -7451,7 +7138,7 @@
 
         dc.onAppTransitionDone();
 
-        changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+        changes |= FINISH_LAYOUT_REDO_LAYOUT;
         if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG_WM,
                 "Wallpaper layer changed: assigning layers + relayout");
         dc.moveInputMethodWindowsIfNeeded(true);
@@ -7536,6 +7223,7 @@
         }
     }
 
+    // TODO: Move to DisplayContent
     boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
         WindowState newFocus = mRoot.computeFocusedWindow();
         if (mCurrentFocus != newFocus) {
@@ -7565,9 +7253,8 @@
             if (imWindowChanged && oldFocus != mInputMethodWindow) {
                 // Focus of the input method window changed. Perform layout if needed.
                 if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
-                    mWindowPlacerLocked.performLayoutLockedInner(displayContent, true /*initial*/,
-                            updateInputWindows);
-                    focusChanged &= ~WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+                    displayContent.performLayout(true /*initial*/,  updateInputWindows);
+                    focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
                 } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                     // Client will do the layout, but we need to assign layers
                     // for handleNewWindowLocked() below.
@@ -7575,12 +7262,11 @@
                 }
             }
 
-            if ((focusChanged & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+            if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
                 // The change in focus caused us to need to do a layout.  Okay.
                 displayContent.setLayoutNeeded();
                 if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
-                    mWindowPlacerLocked.performLayoutLockedInner(displayContent, true /*initial*/,
-                            updateInputWindows);
+                    displayContent.performLayout(true /*initial*/, updateInputWindows);
                 }
             }
 
@@ -7592,11 +7278,10 @@
 
             displayContent.adjustForImeIfNeeded();
 
-            // We may need to schedule some toast windows to be removed. The
-            // toasts for an app that does not have input focus are removed
-            // within a timeout to prevent apps to redress other apps' UI.
-            getDefaultDisplayContentLocked().scheduleToastWindowsTimeoutIfNeededLocked(
-                        oldFocus, newFocus);
+            // We may need to schedule some toast windows to be removed. The toasts for an app that
+            // does not have input focus are removed within a timeout to prevent apps to redress
+            // other apps' UI.
+            displayContent.scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);
 
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             return true;
@@ -8229,8 +7914,9 @@
         }
     }
 
-    boolean dumpWindows(PrintWriter pw, String name, String[] args, int opti, boolean dumpAll) {
-        WindowList windows = new WindowList();
+    private boolean dumpWindows(PrintWriter pw, String name, String[] args, int opti,
+            boolean dumpAll) {
+        final WindowList windows = new WindowList();
         if ("apps".equals(name) || "visible".equals(name) || "visible-apps".equals(name)) {
             final boolean appsOnly = name.contains("apps");
             final boolean visibleOnly = name.contains("visible");
@@ -8257,7 +7943,7 @@
         return true;
     }
 
-    void dumpLastANRLocked(PrintWriter pw) {
+    private void dumpLastANRLocked(PrintWriter pw) {
         pw.println("WINDOW MANAGER LAST ANR (dumpsys window lastanr)");
         if (mLastANRState == null) {
             pw.println("  <no ANR has occurred since boot>");
@@ -8275,8 +7961,7 @@
      * @param windowState The window that ANR'd, may be null.
      * @param reason The reason for the ANR, may be null.
      */
-    public void saveANRStateLocked(AppWindowToken appWindowToken, WindowState windowState,
-            String reason) {
+    void saveANRStateLocked(AppWindowToken appWindowToken, WindowState windowState, String reason) {
         StringWriter sw = new StringWriter();
         PrintWriter pw = new FastPrintWriter(sw, false, 1024);
         pw.println("  ANR time: " + DateFormat.getInstance().format(new Date()));
@@ -8331,6 +8016,7 @@
                 pw.println("    a[animator]: animator state");
                 pw.println("    s[essions]: active sessions");
                 pw.println("    surfaces: active surfaces (debugging enabled only)");
+                pw.println("    pip: PIP state");
                 pw.println("    d[isplays]: active display contents");
                 pw.println("    t[okens]: token list");
                 pw.println("    w[indows]: window list");
@@ -8403,6 +8089,18 @@
                     pw.println(output.toString());
                 }
                 return;
+            } else if ("pip".equals(cmd)) {
+                synchronized(mWindowMap) {
+                    pw.print("defaultBounds=");
+                    getPictureInPictureDefaultBounds(DEFAULT_DISPLAY).printShortString(pw);
+                    pw.println();
+                    pw.print("movementBounds=");
+                    getPictureInPictureMovementBounds(DEFAULT_DISPLAY).printShortString(pw);
+                    pw.println();
+                    getDefaultDisplayContentLocked().getPinnedStackController().dump("", pw);
+                    pw.println();
+                }
+                return;
             } else {
                 // Dumping a single name?
                 if (!dumpWindows(pw, cmd, args, opti, dumpAll)) {
@@ -8474,6 +8172,8 @@
     }
 
     // There is an inherent assumption that this will never return null.
+    // TODO(multi-display): Inspect all the call-points of this method to see if they make sense to
+    // support non-default display.
     DisplayContent getDefaultDisplayContentLocked() {
         return mRoot.getDisplayContentOrCreate(DEFAULT_DISPLAY);
     }
@@ -8677,13 +8377,19 @@
         }
     }
 
+    public void setSupportsPictureInPicture(boolean supportsPictureInPicture) {
+        synchronized (mWindowMap) {
+            mSupportsPictureInPicture = supportsPictureInPicture;
+        }
+    }
+
     static int dipToPixel(int dip, DisplayMetrics displayMetrics) {
         return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
     }
 
     @Override
     public void registerDockedStackListener(IDockedStackListener listener) {
-        if (!checkCallingPermission(android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS,
+        if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS,
                 "registerDockedStackListener()")) {
             return;
         }
@@ -8693,6 +8399,21 @@
     }
 
     @Override
+    public void registerPinnedStackListener(int displayId, IPinnedStackListener listener) {
+        if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS,
+                "registerPinnedStackListener()")) {
+            return;
+        }
+        if (!mSupportsPictureInPicture) {
+            return;
+        }
+        synchronized (mWindowMap) {
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            displayContent.getPinnedStackController().registerPinnedStackListener(listener);
+        }
+    }
+
+    @Override
     public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
         try {
             WindowState focusedWindow = getFocusedWindow();
@@ -8865,8 +8586,7 @@
     @Override
     public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver)
             throws RemoteException {
-        if (!checkCallingPermission(Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS,
-                "registerShortcutKey")) {
+        if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS, "registerShortcutKey")) {
             throw new SecurityException(
                     "Requires REGISTER_WINDOW_MANAGER_LISTENERS permission");
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a7b46111..51fc9f0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -136,9 +136,51 @@
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
 
 class WindowList extends ArrayList<WindowState> {
-    WindowList() {}
-    WindowList(WindowList windowList) {
-        super(windowList);
+
+    /**
+     * Read-only interface for the window list that the creator of the window list can pass-out to
+     * other users to prevent them from modifying the window list.
+     */
+    private ReadOnlyWindowList mReadOnly;
+
+    WindowList() {
+        mReadOnly = new ReadOnlyWindowList(this);
+    }
+
+    /** Returns the read-only interface for this window list. */
+    ReadOnlyWindowList getReadOnly() {
+        return mReadOnly;
+    }
+}
+
+/**
+ * Read-only interface for a list of windows. It is common for the owner of a list of windows to
+ * want to provide a way for external classes to iterate of its windows, but prevent them from
+ * modifying the list in any way. This call provides a way for them to do that by wrapping the
+ * original window list and only exposing the read-only APIs.
+ */
+final class ReadOnlyWindowList {
+    // List of windows this read-only class is tied to.
+    private final WindowList mWindows;
+
+    ReadOnlyWindowList(WindowList windows) {
+        mWindows = windows;
+    }
+
+    WindowState get(int index) {
+        return mWindows.get(index);
+    }
+
+    int indexOf(WindowState w) {
+        return mWindows.indexOf(w);
+    }
+
+    int size() {
+        return mWindows.size();
+    }
+
+    boolean isEmpty() {
+        return mWindows.isEmpty();
     }
 }
 
@@ -689,8 +731,9 @@
     }
 
     @Override
-    public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf,
-            Rect osf) {
+    public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
+            Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
+            Rect outsetFrame) {
         if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
             // This window is being replaced and either already got information that it's being
             // removed or we are still waiting for some information. Because of this we don't
@@ -726,10 +769,10 @@
         final int layoutYDiff;
         if (fullscreenTask || layoutInParentFrame()) {
             // We use the parent frame as the containing frame for fullscreen and child windows
-            mContainingFrame.set(pf);
-            mDisplayFrame.set(df);
-            layoutDisplayFrame = df;
-            layoutContainingFrame = pf;
+            mContainingFrame.set(parentFrame);
+            mDisplayFrame.set(displayFrame);
+            layoutDisplayFrame = displayFrame;
+            layoutContainingFrame = parentFrame;
             layoutXDiff = 0;
             layoutYDiff = 0;
         } else {
@@ -745,15 +788,15 @@
             final WindowState imeWin = mService.mInputMethodWindow;
             // IME is up and obscuring this window. Adjust the window position so it is visible.
             if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this) {
-                    if (windowsAreFloating && mContainingFrame.bottom > cf.bottom) {
+                    if (windowsAreFloating && mContainingFrame.bottom > contentFrame.bottom) {
                         // In freeform we want to move the top up directly.
-                        // TODO: Investigate why this is cf not pf.
-                        mContainingFrame.top -= mContainingFrame.bottom - cf.bottom;
-                    } else if (mContainingFrame.bottom > pf.bottom) {
+                        // TODO: Investigate why this is contentFrame not parentFrame.
+                        mContainingFrame.top -= mContainingFrame.bottom - contentFrame.bottom;
+                    } else if (mContainingFrame.bottom > parentFrame.bottom) {
                         // But in docked we want to behave like fullscreen
                         // and behave as if the task were given smaller bounds
                         // for the purposes of layout.
-                        mContainingFrame.bottom = pf.bottom;
+                        mContainingFrame.bottom = parentFrame.bottom;
                     }
             }
 
@@ -762,7 +805,7 @@
                 // if it wasn't set already. No need to intersect it with the (visible)
                 // "content frame" since it is allowed to be outside the visible desktop.
                 if (mContainingFrame.isEmpty()) {
-                    mContainingFrame.set(cf);
+                    mContainingFrame.set(contentFrame);
                 }
             }
             mDisplayFrame.set(mContainingFrame);
@@ -770,22 +813,22 @@
             layoutYDiff = !mInsetFrame.isEmpty() ? mInsetFrame.top - mContainingFrame.top : 0;
             layoutContainingFrame = !mInsetFrame.isEmpty() ? mInsetFrame : mContainingFrame;
             mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight);
-            subtractInsets(mDisplayFrame, layoutContainingFrame, df, mTmpRect);
+            subtractInsets(mDisplayFrame, layoutContainingFrame, displayFrame, mTmpRect);
             if (!layoutInParentFrame()) {
-                subtractInsets(mContainingFrame, layoutContainingFrame, pf, mTmpRect);
-                subtractInsets(mInsetFrame, layoutContainingFrame, pf, mTmpRect);
+                subtractInsets(mContainingFrame, layoutContainingFrame, parentFrame, mTmpRect);
+                subtractInsets(mInsetFrame, layoutContainingFrame, parentFrame, mTmpRect);
             }
-            layoutDisplayFrame = df;
+            layoutDisplayFrame = displayFrame;
             layoutDisplayFrame.intersect(layoutContainingFrame);
         }
 
         final int pw = mContainingFrame.width();
         final int ph = mContainingFrame.height();
 
-        if (!mParentFrame.equals(pf)) {
+        if (!mParentFrame.equals(parentFrame)) {
             //Slog.i(TAG_WM, "Window " + this + " content frame from " + mParentFrame
-            //        + " to " + pf);
-            mParentFrame.set(pf);
+            //        + " to " + parentFrame);
+            mParentFrame.set(parentFrame);
             mContentChanged = true;
         }
         if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
@@ -794,14 +837,14 @@
             mContentChanged = true;
         }
 
-        mOverscanFrame.set(of);
-        mContentFrame.set(cf);
-        mVisibleFrame.set(vf);
-        mDecorFrame.set(dcf);
-        mStableFrame.set(sf);
-        final boolean hasOutsets = osf != null;
+        mOverscanFrame.set(overscanFrame);
+        mContentFrame.set(contentFrame);
+        mVisibleFrame.set(visibleFrame);
+        mDecorFrame.set(decorFrame);
+        mStableFrame.set(stableFrame);
+        final boolean hasOutsets = outsetFrame != null;
         if (hasOutsets) {
-            mOutsetFrame.set(osf);
+            mOutsetFrame.set(outsetFrame);
         }
 
         final int fw = mFrame.width();
@@ -987,6 +1030,10 @@
         return mVisibleFrame;
     }
 
+    Rect getStableFrameLw() {
+        return mStableFrame;
+    }
+
     @Override
     public boolean getGivenInsetsPendingLw() {
         return mGivenInsetsPending;
@@ -3873,7 +3920,6 @@
             dc.addToWindowList(this, index);
             index++;
         }
-        mService.mWindowsChanged = true;
         return index;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 04e00c4..63820e5 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -213,175 +213,6 @@
         return mInLayout;
     }
 
-    final void performLayoutLockedInner(final DisplayContent displayContent,
-            boolean initial, boolean updateInputWindows) {
-        if (!displayContent.isLayoutNeeded()) {
-            return;
-        }
-        displayContent.clearLayoutNeeded();
-        WindowList windows = displayContent.getWindowList();
-        boolean isDefaultDisplay = displayContent.isDefaultDisplay;
-
-        DisplayInfo displayInfo = displayContent.getDisplayInfo();
-        final int dw = displayInfo.logicalWidth;
-        final int dh = displayInfo.logicalHeight;
-
-        final int N = windows.size();
-        int i;
-
-        if (DEBUG_LAYOUT) {
-            Slog.v(TAG, "-------------------------------------");
-            Slog.v(TAG, "performLayout: needed=" + displayContent.isLayoutNeeded()
-                    + " dw=" + dw + " dh=" + dh);
-        }
-
-        mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mService.mRotation,
-                displayContent.getConfiguration().uiMode);
-        if (isDefaultDisplay) {
-            // Not needed on non-default displays.
-            mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
-            mService.mScreenRect.set(0, 0, dw, dh);
-        }
-
-        mService.mPolicy.getContentRectLw(mTmpContentRect);
-        displayContent.resize(mTmpContentRect);
-
-        int seq = mService.mLayoutSeq+1;
-        if (seq < 0) seq = 0;
-        mService.mLayoutSeq = seq;
-
-        boolean behindDream = false;
-
-        // First perform layout of any root windows (not attached
-        // to another window).
-        int topAttached = -1;
-        for (i = N-1; i >= 0; i--) {
-            final WindowState win = windows.get(i);
-
-            // Don't do layout of a window if it is not visible, or
-            // soon won't be visible, to avoid wasting time and funky
-            // changes while a window is animating away.
-            final boolean gone = (behindDream && mService.mPolicy.canBeForceHidden(win, win.mAttrs))
-                    || win.isGoneForLayoutLw();
-
-            if (DEBUG_LAYOUT && !win.mLayoutAttached) {
-                Slog.v(TAG, "1ST PASS " + win
-                        + ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame
-                        + " mLayoutAttached=" + win.mLayoutAttached
-                        + " screen changed=" + win.isConfigChanged());
-                final AppWindowToken atoken = win.mAppToken;
-                if (gone) Slog.v(TAG, "  GONE: mViewVisibility="
-                        + win.mViewVisibility + " mRelayoutCalled="
-                        + win.mRelayoutCalled + " hidden="
-                        + win.mToken.hidden + " hiddenRequested="
-                        + (atoken != null && atoken.hiddenRequested)
-                        + " parentHidden=" + win.isParentWindowHidden());
-                else Slog.v(TAG, "  VIS: mViewVisibility="
-                        + win.mViewVisibility + " mRelayoutCalled="
-                        + win.mRelayoutCalled + " hidden="
-                        + win.mToken.hidden + " hiddenRequested="
-                        + (atoken != null && atoken.hiddenRequested)
-                        + " parentHidden=" + win.isParentWindowHidden());
-            }
-
-            // If this view is GONE, then skip it -- keep the current
-            // frame, and let the caller know so they can ignore it
-            // if they want.  (We do the normal layout for INVISIBLE
-            // windows, since that means "perform layout as normal,
-            // just don't display").
-            if (!gone || !win.mHaveFrame || win.mLayoutNeeded
-                    || ((win.isConfigChanged() || win.setReportResizeHints())
-                            && !win.isGoneForLayoutLw() &&
-                            ((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 ||
-                            (win.mHasSurface && win.mAppToken != null &&
-                            win.mAppToken.layoutConfigChanges)))) {
-                if (!win.mLayoutAttached) {
-                    if (initial) {
-                        //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
-                        win.mContentChanged = false;
-                    }
-                    if (win.mAttrs.type == TYPE_DREAM) {
-                        // Don't layout windows behind a dream, so that if it
-                        // does stuff like hide the status bar we won't get a
-                        // bad transition when it goes away.
-                        behindDream = true;
-                    }
-                    win.mLayoutNeeded = false;
-                    win.prelayout();
-                    mService.mPolicy.layoutWindowLw(win, null);
-                    win.mLayoutSeq = seq;
-
-                    // Window frames may have changed. Update dim layer with the new bounds.
-                    final Task task = win.getTask();
-                    if (task != null) {
-                        displayContent.mDimLayerController.updateDimLayer(task);
-                    }
-
-                    if (DEBUG_LAYOUT) Slog.v(TAG,
-                            "  LAYOUT: mFrame="
-                            + win.mFrame + " mContainingFrame="
-                            + win.mContainingFrame + " mDisplayFrame="
-                            + win.mDisplayFrame);
-                } else {
-                    if (topAttached < 0) topAttached = i;
-                }
-            }
-        }
-
-        boolean attachedBehindDream = false;
-
-        // Now perform layout of attached windows, which usually
-        // depend on the position of the window they are attached to.
-        // XXX does not deal with windows that are attached to windows
-        // that are themselves attached.
-        for (i = topAttached; i >= 0; i--) {
-            final WindowState win = windows.get(i);
-
-            if (win.mLayoutAttached) {
-                if (DEBUG_LAYOUT) Slog.v(TAG,
-                        "2ND PASS " + win + " mHaveFrame=" + win.mHaveFrame + " mViewVisibility="
-                        + win.mViewVisibility + " mRelayoutCalled=" + win.mRelayoutCalled);
-                // If this view is GONE, then skip it -- keep the current
-                // frame, and let the caller know so they can ignore it
-                // if they want.  (We do the normal layout for INVISIBLE
-                // windows, since that means "perform layout as normal,
-                // just don't display").
-                if (attachedBehindDream && mService.mPolicy.canBeForceHidden(win, win.mAttrs)) {
-                    continue;
-                }
-                if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
-                        || !win.mHaveFrame || win.mLayoutNeeded) {
-                    if (initial) {
-                        //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
-                        win.mContentChanged = false;
-                    }
-                    win.mLayoutNeeded = false;
-                    win.prelayout();
-                    mService.mPolicy.layoutWindowLw(win, win.getParentWindow());
-                    win.mLayoutSeq = seq;
-                    if (DEBUG_LAYOUT) Slog.v(TAG,
-                            "  LAYOUT: mFrame=" + win.mFrame + " mContainingFrame="
-                            + win.mContainingFrame + " mDisplayFrame=" + win.mDisplayFrame);
-                }
-            } else if (win.mAttrs.type == TYPE_DREAM) {
-                // Don't layout windows behind a dream, so that if it
-                // does stuff like hide the status bar we won't get a
-                // bad transition when it goes away.
-                attachedBehindDream = behindDream;
-            }
-        }
-
-        // Window frames may have changed. Tell the input dispatcher about it.
-        mService.mInputMonitor.layoutInputConsumers(dw, dh);
-        mService.mInputMonitor.setUpdateInputWindowsNeededLw();
-        if (updateInputWindows) {
-            mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
-        }
-
-        mService.mPolicy.finishLayoutLw();
-        mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
-    }
-
     /**
      * @return bitmap indicating if another pass through layout must be made.
      */
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 38225cc..38f25e0 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -235,7 +235,6 @@
         if (!mChildren.contains(win)) {
             addChild(win, null);
         }
-        mService.mWindowsChanged = true;
         mDisplayContent.moveInputMethodDialogs(pos + 1);
     }
 
@@ -338,8 +337,9 @@
         }
     }
 
-    boolean updateWallpaperWindowsPlacement(WindowList windowList, WindowState wallpaperTarget,
-            int wallpaperTargetIndex, boolean visible, int dw, int dh, int wallpaperAnimLayerAdj) {
+    boolean updateWallpaperWindowsPlacement(ReadOnlyWindowList windowList,
+            WindowState wallpaperTarget, int wallpaperTargetIndex, boolean visible, int dw, int dh,
+            int wallpaperAnimLayerAdj) {
 
         boolean changed = false;
         if (hidden == visible) {
@@ -379,8 +379,7 @@
             if (oldIndex >= 0) {
                 if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
                         "Wallpaper removing at " + oldIndex + ": " + wallpaper);
-                windowList.remove(oldIndex);
-                mService.mWindowsChanged = true;
+                mDisplayContent.removeFromWindowList(wallpaper);
                 if (oldIndex < wallpaperTargetIndex) {
                     wallpaperTargetIndex--;
                 }
@@ -403,8 +402,7 @@
                     || (DEBUG_ADD_REMOVE && oldIndex != insertionIndex)) Slog.v(TAG,
                     "Moving wallpaper " + wallpaper + " from " + oldIndex + " to " + insertionIndex);
 
-            windowList.add(insertionIndex, wallpaper);
-            mService.mWindowsChanged = true;
+            mDisplayContent.addToWindowList(wallpaper, insertionIndex);
             changed = true;
         }
 
@@ -415,7 +413,7 @@
      * @return The index in {@param windows} of the lowest window that is currently on screen and
      *         not hidden by the policy.
      */
-    private int findLowestWindowOnScreen(WindowList windowList) {
+    private int findLowestWindowOnScreen(ReadOnlyWindowList windowList) {
         final int size = windowList.size();
         for (int index = 0; index < size; index++) {
             final WindowState win = windowList.get(index);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 270488c..1ecb3d3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -179,7 +179,7 @@
  */
 public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
 
-    private static final String LOG_TAG = "DevicePolicyManagerService";
+    private static final String LOG_TAG = "DevicePolicyManager";
 
     private static final boolean VERBOSE_LOG = false; // DO NOT SUBMIT WITH TRUE
 
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 3548f28..4165467 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -4,9 +4,9 @@
      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.
@@ -30,6 +30,7 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.REAL_GET_TASKS" />
     <uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
+    <uses-permission android:name="android.permission.REORDER_TASKS" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
     <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
@@ -159,6 +160,9 @@
 
         <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityA" />
         <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityB" />
+        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
+        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" />
+        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" />
 
     </application>
 
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java b/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
index b47d17e..c9c691d 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
@@ -17,21 +17,33 @@
 package com.android.server.am;
 
 import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
+import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
 import android.app.ITaskStackListener;
+import android.app.Instrumentation.ActivityMonitor;
+import android.app.TaskStackListener;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.Resources.Theme;
 import android.os.RemoteException;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiDevice;
-
+import android.text.TextUtils;
+import android.util.Pair;
 import com.android.internal.annotations.GuardedBy;
-
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -39,9 +51,10 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
-public class TaskStackChangedListenerTest extends ITaskStackListener.Stub {
+public class TaskStackChangedListenerTest {
 
     private IActivityManager mService;
+    private ITaskStackListener mTaskStackListener;
 
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
@@ -51,11 +64,25 @@
     @Before
     public void setUp() throws Exception {
         mService = ActivityManagerNative.getDefault();
-        mService.registerTaskStackListener(this);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mService.unregisterTaskStackListener(mTaskStackListener);
+        mTaskStackListener = null;
     }
 
     @Test
     public void testTaskStackChanged_afterFinish() throws Exception {
+        registerTaskStackChangedListener(new TaskStackListener() {
+            @Override
+            public void onTaskStackChanged() throws RemoteException {
+                synchronized (sLock) {
+                    sTaskStackChangedCalled = true;
+                }
+            }
+        });
+
         Context ctx = InstrumentationRegistry.getContext();
         ctx.startActivity(new Intent(ctx, ActivityA.class));
         UiDevice.getInstance(getInstrumentation()).waitForIdle();
@@ -65,31 +92,151 @@
         Assert.assertTrue(sActivityBResumed);
     }
 
-    @Override
-    public void onTaskStackChanged() throws RemoteException {
-        synchronized (sLock) {
-            sTaskStackChangedCalled = true;
+    @Test
+    public void testTaskDescriptionChanged() throws Exception {
+        final Object[] params = new Object[2];
+        final CountDownLatch latch = new CountDownLatch(1);
+        registerTaskStackChangedListener(new TaskStackListener() {
+            int taskId = -1;
+
+            @Override
+            public void onTaskCreated(int taskId, ComponentName componentName)
+                    throws RemoteException {
+                this.taskId = taskId;
+            }
+            @Override
+            public void onTaskDescriptionChanged(int taskId, TaskDescription td)
+                    throws RemoteException {
+                if (this.taskId == taskId && !TextUtils.isEmpty(td.getLabel())) {
+                    params[0] = taskId;
+                    params[1] = td;
+                    latch.countDown();
+                }
+            }
+        });
+        final Activity activity = startTestActivity(ActivityTaskDescriptionChange.class);
+        waitForCallback(latch);
+        assertEquals(activity.getTaskId(), params[0]);
+        assertEquals("Test Label", ((TaskDescription) params[1]).getLabel());
+    }
+
+    @Test
+    public void testActivityRequestedOrientationChanged() throws Exception {
+        final int[] params = new int[2];
+        final CountDownLatch latch = new CountDownLatch(1);
+        registerTaskStackChangedListener(new TaskStackListener() {
+            @Override
+            public void onActivityRequestedOrientationChanged(int taskId,
+                    int requestedOrientation) {
+                params[0] = taskId;
+                params[1] = requestedOrientation;
+                latch.countDown();
+            }
+        });
+        final Activity activity = startTestActivity(ActivityRequestedOrientationChange.class);
+        waitForCallback(latch);
+        assertEquals(activity.getTaskId(), params[0]);
+        assertEquals(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, params[1]);
+    }
+
+    @Test
+    /**
+     * Tests for onTaskCreated, onTaskMovedToFront, onTaskRemoved and onTaskRemovalStarted.
+     */
+    public void testTaskChangeCallBacks() throws Exception {
+        final Object[] params = new Object[2];
+        final CountDownLatch taskCreatedLaunchLatch = new CountDownLatch(1);
+        final CountDownLatch taskMovedToFrontLatch = new CountDownLatch(1);
+        final CountDownLatch taskRemovedLatch = new CountDownLatch(1);
+        final CountDownLatch taskRemovalStartedLatch = new CountDownLatch(1);
+        final CountDownLatch onDetachedFromWindowLatch = new CountDownLatch(1);
+        registerTaskStackChangedListener(new TaskStackListener() {
+            @Override
+            public void onTaskCreated(int taskId, ComponentName componentName)
+                    throws RemoteException {
+                params[0] = taskId;
+                params[1] = componentName;
+                taskCreatedLaunchLatch.countDown();
+            }
+
+            @Override
+            public void onTaskMovedToFront(int taskId) throws RemoteException {
+                params[0] = taskId;
+                taskMovedToFrontLatch.countDown();
+            }
+
+            @Override
+            public void onTaskRemovalStarted(int taskId) {
+                params[0] = taskId;
+                taskRemovalStartedLatch.countDown();
+            }
+
+            @Override
+            public void onTaskRemoved(int taskId) throws RemoteException {
+                params[0] = taskId;
+                taskRemovedLatch.countDown();
+            }
+        });
+
+        final ActivityTaskChangeCallbacks activity =
+                (ActivityTaskChangeCallbacks) startTestActivity(ActivityTaskChangeCallbacks.class);
+        final int id = activity.getTaskId();
+
+        // Test for onTaskCreated.
+        waitForCallback(taskCreatedLaunchLatch);
+        assertEquals(id, params[0]);
+        ComponentName componentName = (ComponentName) params[1];
+        assertEquals(ActivityTaskChangeCallbacks.class.getName(), componentName.getClassName());
+
+        // Test for onTaskMovedToFront.
+        assertEquals(1, taskMovedToFrontLatch.getCount());
+        mService.moveTaskToFront(id, 0, null);
+        waitForCallback(taskMovedToFrontLatch);
+        assertEquals(activity.getTaskId(), params[0]);
+
+        // Test for onTaskRemovalStarted.
+        assertEquals(1, taskRemovalStartedLatch.getCount());
+        activity.finishAndRemoveTask();
+        waitForCallback(taskRemovalStartedLatch);
+        // onTaskRemovalStarted happens before the activity's window is removed.
+        assertFalse(activity.onDetachedFromWindowCalled);
+        assertEquals(id, params[0]);
+
+        // Test for onTaskRemoved.
+        assertEquals(1, taskRemovedLatch.getCount());
+        waitForCallback(taskRemovedLatch);
+        assertEquals(id, params[0]);
+        assertTrue(activity.onDetachedFromWindowCalled);
+    }
+
+    /**
+     * Starts the provided activity and returns the started instance.
+     */
+    private Activity startTestActivity(Class<?> activityClass) {
+        final Context context = InstrumentationRegistry.getContext();
+        final ActivityMonitor monitor =
+                new ActivityMonitor(activityClass.getName(), null, false);
+        InstrumentationRegistry.getInstrumentation().addMonitor(monitor);
+        context.startActivity(new Intent(context, activityClass));
+        final Activity activity = monitor.waitForActivityWithTimeout(1000);
+        if (activity == null) {
+            throw new RuntimeException("Timed out waiting for Activity");
         }
+        return activity;
     }
 
-    @Override
-    public void onActivityPinned() throws RemoteException {
+    private void registerTaskStackChangedListener(ITaskStackListener listener) throws Exception {
+        mTaskStackListener = listener;
+        mService.registerTaskStackListener(listener);
     }
 
-    @Override
-    public void onPinnedActivityRestartAttempt() throws RemoteException {
-    }
-
-    @Override
-    public void onPinnedStackAnimationEnded() throws RemoteException {
-    }
-
-    @Override
-    public void onActivityForcedResizable(String packageName, int taskId) throws RemoteException {
-    }
-
-    @Override
-    public void onActivityDismissingDockedStack() throws RemoteException {
+    private void waitForCallback(CountDownLatch latch) {
+        try {
+        final boolean result = latch.await(2, TimeUnit.SECONDS);
+            if (!result) {
+                throw new RuntimeException("Timed out waiting for task stack change notification");
+            }
+        }catch (InterruptedException e) {}
     }
 
     public static class ActivityA extends Activity {
@@ -120,4 +267,31 @@
             finish();
         }
     }
+
+    public static class ActivityRequestedOrientationChange extends Activity {
+        @Override
+        protected void onPostResume() {
+            super.onPostResume();
+            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+            finish();
+        }
+    }
+
+    public static class ActivityTaskDescriptionChange extends Activity {
+        @Override
+        protected void onPostResume() {
+            super.onPostResume();
+            setTaskDescription(new TaskDescription("Test Label"));
+            finish();
+        }
+    }
+
+    public static class ActivityTaskChangeCallbacks extends Activity {
+        public boolean onDetachedFromWindowCalled = false;;
+
+        @Override
+        public void onDetachedFromWindow() {
+            onDetachedFromWindowCalled = true;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index 128317c..0ccd0ad 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -42,9 +42,8 @@
 /**
  * Test class for {@link WindowContainer}.
  *
- * Build: mmma -j32 frameworks/base/services/tests/servicestests
- * Install: adb install -r out/target/product/$TARGET_PRODUCT/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
- * Run: adb shell am instrument -w -e class com.android.server.wm.WindowContainerTests com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.wm.WindowContainerTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
new file mode 100644
index 0000000..19b4186
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Gravity;
+import android.view.IWindow;
+import android.view.WindowManager;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
+import static android.view.WindowManager.LayoutParams.FILL_PARENT;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery.
+ *
+ * Build/Install/Run: bit FrameworksServicesTests:com.android.server.wm.WindowFrameTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class WindowFrameTests {
+
+    private static WindowManagerService sWm = null;
+    private WindowToken mWindowToken;
+    private final IWindow mIWindow = new TestIWindow();
+
+    class WindowStateWithTask extends WindowState {
+        final Task mTask;
+        WindowStateWithTask(WindowManager.LayoutParams attrs, Task t) {
+            super(sWm, null, mIWindow, mWindowToken, null, 0, 0, attrs, 0, 0);
+            mTask = t;
+        }
+
+        @Override
+        Task getTask() {
+            return mTask;
+        }
+    };
+
+    class TaskWithBounds extends Task {
+        final Rect mBounds;
+        TaskWithBounds(Rect bounds) {
+            super(0, mStubStack, 0, sWm, null, null, false);
+            mBounds = bounds;
+        }
+        @Override
+        void getBounds(Rect outBounds) {
+            outBounds.set(mBounds);
+        }
+        @Override
+        void getTempInsetBounds(Rect outBounds) {
+            outBounds.setEmpty();
+        }
+        @Override
+        boolean isFullscreen() {
+            return true;
+        }
+    }
+
+    TaskStack mStubStack;
+
+    @Before
+    public void setUp() throws Exception {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        sWm = TestWindowManagerPolicy.getWindowManagerService(context);
+        mWindowToken = new WindowToken(sWm, new Binder(), 0, false,
+                sWm.getDefaultDisplayContentLocked());
+        mStubStack = new TaskStack(sWm, 0);
+    }
+
+    public void assertRect(Rect rect, int left, int top, int right, int bottom) {
+        assertEquals(left, rect.left);
+        assertEquals(top, rect.top);
+        assertEquals(right, rect.right);
+        assertEquals(bottom, rect.bottom);
+    }
+
+    @Test
+    public void testLayoutInFullscreenTaskInsets() throws Exception {
+        Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
+        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
+
+        final int bottomContentInset = 100;
+        final int topContentInset = 50;
+        final int bottomVisibleInset = 30;
+        final int topVisibleInset = 70;
+        final int leftStableInset = 20;
+        final int rightStableInset = 90;
+
+        // With no insets or system decor all the frames incoming from PhoneWindowManager
+        // are identical.
+        final Rect pf = new Rect(0, 0, 1000, 1000);
+        final Rect df = pf;
+        final Rect of = df;
+        final Rect cf = new Rect(pf);
+        // Produce some insets
+        cf.top += 50;
+        cf.bottom -= 100;
+        final Rect vf = new Rect(pf);
+        vf.top += topVisibleInset;
+        vf.bottom -= bottomVisibleInset;
+        final Rect sf = new Rect(pf);
+        sf.left += leftStableInset;
+        sf.right -= rightStableInset;
+
+        final Rect dcf = pf;
+        // When mFrame extends past cf, the content insets are
+        // the difference between mFrame and ContentFrame. Visible
+        // and stable frames work the same way.
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
+        assertRect(w.mFrame,0, 0, 1000, 1000);
+        assertRect(w.mContentInsets, 0, topContentInset, 0, bottomContentInset);
+        assertRect(w.mVisibleInsets, 0, topVisibleInset, 0, bottomVisibleInset);
+        assertRect(w.mStableInsets, leftStableInset, 0, rightStableInset, 0);
+        // The frames remain as passed in shrunk to the window frame
+        assertTrue(cf.equals(w.getContentFrameLw()));
+        assertTrue(vf.equals(w.getVisibleFrameLw()));
+        assertTrue(sf.equals(w.getStableFrameLw()));
+        // On the other hand mFrame doesn't extend past cf we won't get any insets
+        w.mAttrs.x = 100;
+        w.mAttrs.y = 100;
+        w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
+        w.mRequestedWidth = 100;
+        w.mRequestedHeight = 100;
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
+        assertRect(w.mFrame, 100, 100, 200, 200);
+        assertRect(w.mContentInsets, 0, 0, 0, 0);
+        // In this case the frames are shrunk to the window frame.
+        assertTrue(w.mFrame.equals(w.getContentFrameLw()));
+        assertTrue(w.mFrame.equals(w.getVisibleFrameLw()));
+        assertTrue(w.mFrame.equals(w.getStableFrameLw()));
+    }
+
+    @Test
+    public void testLayoutInFullscreenTaskNoInsets() throws Exception {
+        Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
+        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
+
+        // With no insets or system decor all the frames incoming from PhoneWindowManager
+        // are identical.
+        final Rect pf = new Rect(0, 0, 1000, 1000);
+
+        // Here the window has FILL_PARENT, FILL_PARENT
+        // so we expect it to fill the entire available frame.
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        assertRect(w.mFrame, 0, 0, 1000, 1000);
+
+        // It can select various widths and heights within the bounds.
+        // Strangely the window attribute width is ignored for normal windows
+        // and we use mRequestedWidth/mRequestedHeight
+        w.mAttrs.width = 300;
+        w.mAttrs.height = 300;
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        // Explicit width and height without requested width/height
+        // gets us nothing.
+        assertRect(w.mFrame, 0, 0, 0, 0);
+
+        w.mRequestedWidth = 300;
+        w.mRequestedHeight = 300;
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        // With requestedWidth/Height we can freely choose our size within the
+        // parent bounds.
+        assertRect(w.mFrame, 0, 0, 300, 300);
+
+        // With FLAG_SCALED though, requestedWidth/height is used to control
+        // the unscaled surface size, and mAttrs.width/height becomes the
+        // layout controller.
+        w.mAttrs.flags = WindowManager.LayoutParams.FLAG_SCALED;
+        w.mRequestedHeight = -1;
+        w.mRequestedWidth = -1;
+        w.mAttrs.width = 100;
+        w.mAttrs.height = 100;
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        assertRect(w.mFrame, 0, 0, 100, 100);
+        w.mAttrs.flags = 0;
+
+        // But sizes too large will be clipped to the containing frame
+        w.mRequestedWidth = 1200;
+        w.mRequestedHeight = 1200;
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        assertRect(w.mFrame, 0, 0, 1000, 1000);
+
+        // Before they are clipped though windows will be shifted
+        w.mAttrs.x = 300;
+        w.mAttrs.y = 300;
+        w.mRequestedWidth = 1000;
+        w.mRequestedHeight = 1000;
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        assertRect(w.mFrame, 0, 0, 1000, 1000);
+
+        // If there is room to move around in the parent frame the window will be shifted according
+        // to gravity.
+        w.mAttrs.x = 0;
+        w.mAttrs.y = 0;
+        w.mRequestedWidth = 300;
+        w.mRequestedHeight = 300;
+        w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        assertRect(w.mFrame, 700, 0, 1000, 300);
+        w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        assertRect(w.mFrame, 700, 700, 1000, 1000);
+        // Window specified  x and y are interpreted as offsets in the opposite
+        // direction of gravity
+        w.mAttrs.x = 100;
+        w.mAttrs.y = 100;
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        assertRect(w.mFrame, 600, 600, 900, 900);
+    }
+
+    private WindowState createWindow(Task task, int width, int height) {
+        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
+        attrs.width = width;
+        attrs.height = height;
+
+        return new WindowStateWithTask(attrs, task);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index db5b119..9ec546e 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -591,6 +591,20 @@
     }
 
     @Override
+    public void registerPinnedStackListener(int displayId, IPinnedStackListener listener) throws RemoteException {
+    }
+
+    @Override
+    public Rect getPictureInPictureDefaultBounds(int displayId) {
+        return null;
+    }
+
+    @Override
+    public Rect getPictureInPictureMovementBounds(int displayId)  {
+        return null;
+    }
+
+    @Override
     public void setResizeDimLayer(boolean visible, int targetStackId, float alpha)
             throws RemoteException {
     }