Merge "Fix tuner-related crashes"
diff --git a/api/system-current.txt b/api/system-current.txt
index dd6a2839..8729d84 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -20306,6 +20306,7 @@
     method public static void incrementOperationCount(int, int);
     method public static void setThreadStatsTag(int);
     method public static void setThreadStatsTagBackup();
+    method public static void setThreadStatsTagRestore();
     method public static void setThreadStatsUid(int);
     method public static void tagSocket(java.net.Socket) throws java.net.SocketException;
     method public static void untagSocket(java.net.Socket) throws java.net.SocketException;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index ac54960..351064a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1531,14 +1531,90 @@
         }
     }
 
+    /**
+     * Metadata related to the {@link TaskThumbnail}.
+     *
+     * @hide
+     */
+    public static class TaskThumbnailInfo implements Parcelable {
+        /** @hide */
+        public static final String ATTR_TASK_THUMBNAILINFO_PREFIX = "task_thumbnailinfo_";
+        private static final String ATTR_TASK_WIDTH =
+                ATTR_TASK_THUMBNAILINFO_PREFIX + "task_width";
+        private static final String ATTR_TASK_HEIGHT =
+                ATTR_TASK_THUMBNAILINFO_PREFIX + "task_height";
+        private static final String ATTR_SCREEN_ORIENTATION =
+                ATTR_TASK_THUMBNAILINFO_PREFIX + "screen_orientation";
+
+        public int taskWidth;
+        public int taskHeight;
+        public int screenOrientation;
+
+        public TaskThumbnailInfo() {
+            // Do nothing
+        }
+
+        private TaskThumbnailInfo(Parcel source) {
+            readFromParcel(source);
+        }
+
+        /** @hide */
+        public void saveToXml(XmlSerializer out) throws IOException {
+            out.attribute(null, ATTR_TASK_WIDTH, Integer.toString(taskWidth));
+            out.attribute(null, ATTR_TASK_HEIGHT, Integer.toString(taskHeight));
+            out.attribute(null, ATTR_SCREEN_ORIENTATION, Integer.toString(screenOrientation));
+        }
+
+        /** @hide */
+        public void restoreFromXml(String attrName, String attrValue) {
+            if (ATTR_TASK_WIDTH.equals(attrName)) {
+                taskWidth = Integer.parseInt(attrValue);
+            } else if (ATTR_TASK_HEIGHT.equals(attrName)) {
+                taskHeight = Integer.parseInt(attrValue);
+            } else if (ATTR_SCREEN_ORIENTATION.equals(attrName)) {
+                screenOrientation = Integer.parseInt(attrValue);
+            }
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(taskWidth);
+            dest.writeInt(taskHeight);
+            dest.writeInt(screenOrientation);
+        }
+
+        public void readFromParcel(Parcel source) {
+            taskWidth = source.readInt();
+            taskHeight = source.readInt();
+            screenOrientation = source.readInt();
+        }
+
+        public static final Creator<TaskThumbnailInfo> CREATOR = new Creator<TaskThumbnailInfo>() {
+            public TaskThumbnailInfo createFromParcel(Parcel source) {
+                return new TaskThumbnailInfo(source);
+            }
+            public TaskThumbnailInfo[] newArray(int size) {
+                return new TaskThumbnailInfo[size];
+            }
+        };
+    }
+
     /** @hide */
     public static class TaskThumbnail implements Parcelable {
         public Bitmap mainThumbnail;
         public ParcelFileDescriptor thumbnailFileDescriptor;
+        public TaskThumbnailInfo thumbnailInfo;
 
         public TaskThumbnail() {
         }
 
+        private TaskThumbnail(Parcel source) {
+            readFromParcel(source);
+        }
+
         public int describeContents() {
             if (thumbnailFileDescriptor != null) {
                 return thumbnailFileDescriptor.describeContents();
@@ -1559,6 +1635,12 @@
             } else {
                 dest.writeInt(0);
             }
+            if (thumbnailInfo != null) {
+                dest.writeInt(1);
+                thumbnailInfo.writeToParcel(dest, flags);
+            } else {
+                dest.writeInt(0);
+            }
         }
 
         public void readFromParcel(Parcel source) {
@@ -1572,6 +1654,11 @@
             } else {
                 thumbnailFileDescriptor = null;
             }
+            if (source.readInt() != 0) {
+                thumbnailInfo = TaskThumbnailInfo.CREATOR.createFromParcel(source);
+            } else {
+                thumbnailInfo = null;
+            }
         }
 
         public static final Creator<TaskThumbnail> CREATOR = new Creator<TaskThumbnail>() {
@@ -1582,10 +1669,6 @@
                 return new TaskThumbnail[size];
             }
         };
-
-        private TaskThumbnail(Parcel source) {
-            readFromParcel(source);
-        }
     }
 
     /** @hide */
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index bb08be2..2137c3a 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -87,12 +87,21 @@
     public static final int TAG_SYSTEM_MEDIA = 0xFFFFFF02;
 
     /**
-     * Default tag value for {@link BackupManager} traffic.
+     * Default tag value for {@link BackupManager} backup traffic; that is,
+     * traffic from the device to the storage backend.
      *
      * @hide
      */
     public static final int TAG_SYSTEM_BACKUP = 0xFFFFFF03;
 
+    /**
+     * Default tag value for {@link BackupManager} restore traffic; that is,
+     * app data retrieved from the storage backend at install time.
+     *
+     * @hide
+     */
+    public static final int TAG_SYSTEM_RESTORE = 0xFFFFFF04;
+
     private static INetworkStatsService sStatsService;
 
     private synchronized static INetworkStatsService getStatsService() {
@@ -142,6 +151,16 @@
     }
 
     /**
+     * System API for restore-related support components to tag network traffic
+     * appropriately.
+     * @hide
+     */
+    @SystemApi
+    public static void setThreadStatsTagRestore() {
+        setThreadStatsTag(TAG_SYSTEM_RESTORE);
+    }
+
+    /**
      * Get the active tag used when accounting {@link Socket} traffic originating
      * from the current thread. Only one active tag per thread is supported.
      * {@link #tagSocket(Socket)}.
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index a8f8134..6d3dfac 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -240,11 +240,15 @@
     return count;
 }
 
+// The SkiaCanvas::restore operation layers on the capability to preserve
+// either (or both) the matrix and/or clip state after a SkCanvas::restore
+// operation. It does this by explicitly saving off the clip & matrix state
+// when requested and playing it back after the SkCanvas::restore.
 void SkiaCanvas::restore() {
     const SaveRec* rec = (NULL == mSaveStack.get())
             ? NULL
             : static_cast<SaveRec*>(mSaveStack->back());
-    int currentSaveCount = mCanvas->getSaveCount() - 1;
+    int currentSaveCount = mCanvas->getSaveCount();
     SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
 
     if (NULL == rec || rec->saveCount != currentSaveCount) {
@@ -262,8 +266,9 @@
     }
 
     SkTArray<SkClipStack::Element> savedClips;
+    int topClipStackFrame = mCanvas->getClipStack()->getSaveCount();
     if (preserveClip) {
-        saveClipsForFrame(savedClips, currentSaveCount);
+        saveClipsForFrame(savedClips, topClipStackFrame);
     }
 
     mCanvas->restore();
@@ -272,7 +277,11 @@
         mCanvas->setMatrix(savedMatrix);
     }
 
-    if (preserveClip && !savedClips.empty()) {
+    if (preserveClip && !savedClips.empty() &&
+        topClipStackFrame != mCanvas->getClipStack()->getSaveCount()) {
+        // Only reapply the saved clips if the top clip stack frame was actually
+        // popped by restore().  If it wasn't, it means it doesn't belong to the
+        // restored canvas frame (SkCanvas lazy save/restore kicked in).
         applyClips(savedClips);
     }
 
@@ -322,21 +331,23 @@
     }
 
     SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
-    // Store the save counter in the SkClipStack domain.
-    // (0-based, equal to the number of save ops on the stack).
-    rec->saveCount = mCanvas->getSaveCount() - 1;
+    rec->saveCount = mCanvas->getSaveCount();
     rec->saveFlags = flags;
 }
 
-void SkiaCanvas::saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount) {
+void SkiaCanvas::saveClipsForFrame(SkTArray<SkClipStack::Element>& clips,
+                                   int saveCountToBackup) {
+    // Each SkClipStack::Element stores the index of the canvas save
+    // with which it is associated. Backup only those Elements that
+    // are associated with 'saveCountToBackup'
     SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
                                    SkClipStack::Iter::kTop_IterStart);
-    while (const SkClipStack::Element* elem = clipIterator.next()) {
-        if (elem->getSaveCount() < frameSaveCount) {
-            // done with the current frame.
+    while (const SkClipStack::Element* elem = clipIterator.prev()) {
+        if (elem->getSaveCount() < saveCountToBackup) {
+            // done with the target save count.
             break;
         }
-        SkASSERT(elem->getSaveCount() == frameSaveCount);
+        SkASSERT(elem->getSaveCount() == saveCountToBackup);
         clips.push_back(*elem);
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
new file mode 100644
index 0000000..f03e94d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2015 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.location;
+
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Retrieves the information of applications which accessed location recently.
+ */
+public class RecentLocationApps {
+    private static final String TAG = RecentLocationApps.class.getSimpleName();
+    private static final String ANDROID_SYSTEM_PACKAGE_NAME = "android";
+
+    private static final int RECENT_TIME_INTERVAL_MILLIS = 15 * 60 * 1000;
+
+    private static final int[] LOCATION_OPS = new int[] {
+            AppOpsManager.OP_MONITOR_LOCATION,
+            AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
+    };
+
+    private final PackageManager mPackageManager;
+    private final Context mContext;
+
+    public RecentLocationApps(Context context) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+    }
+
+    /**
+     * Fills a list of applications which queried location recently within specified time.
+     */
+    public List<Request> getAppList() {
+        // Retrieve a location usage list from AppOps
+        AppOpsManager aoManager =
+                (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_OPS);
+
+        // Process the AppOps list and generate a preference list.
+        ArrayList<Request> requests = new ArrayList<>(appOps.size());
+        final long now = System.currentTimeMillis();
+        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        final List<UserHandle> profiles = um.getUserProfiles();
+
+        final int appOpsN = appOps.size();
+        for (int i = 0; i < appOpsN; ++i) {
+            AppOpsManager.PackageOps ops = appOps.get(i);
+            // Don't show the Android System in the list - it's not actionable for the user.
+            // Also don't show apps belonging to background users except managed users.
+            String packageName = ops.getPackageName();
+            int uid = ops.getUid();
+            int userId = UserHandle.getUserId(uid);
+            boolean isAndroidOs =
+                    (uid == Process.SYSTEM_UID) && ANDROID_SYSTEM_PACKAGE_NAME.equals(packageName);
+            if (isAndroidOs || !profiles.contains(new UserHandle(userId))) {
+                continue;
+            }
+            Request request = getRequestFromOps(now, ops);
+            if (request != null) {
+                requests.add(request);
+            }
+        }
+
+        return requests;
+    }
+
+    /**
+     * Creates a Request entry for the given PackageOps.
+     *
+     * This method examines the time interval of the PackageOps first. If the PackageOps is older
+     * than the designated interval, this method ignores the PackageOps object and returns null.
+     * When the PackageOps is fresh enough, this method returns a Request object for the package
+     */
+    private Request getRequestFromOps(long now,
+            AppOpsManager.PackageOps ops) {
+        String packageName = ops.getPackageName();
+        List<AppOpsManager.OpEntry> entries = ops.getOps();
+        boolean highBattery = false;
+        boolean normalBattery = false;
+        // Earliest time for a location request to end and still be shown in list.
+        long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
+        for (AppOpsManager.OpEntry entry : entries) {
+            if (entry.isRunning() || entry.getTime() >= recentLocationCutoffTime) {
+                switch (entry.getOp()) {
+                    case AppOpsManager.OP_MONITOR_LOCATION:
+                        normalBattery = true;
+                        break;
+                    case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
+                        highBattery = true;
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+
+        if (!highBattery && !normalBattery) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, packageName + " hadn't used location within the time interval.");
+            }
+            return null;
+        }
+
+        // The package is fresh enough, continue.
+
+        int uid = ops.getUid();
+        int userId = UserHandle.getUserId(uid);
+
+        Request request = null;
+        try {
+            IPackageManager ipm = AppGlobals.getPackageManager();
+            ApplicationInfo appInfo =
+                    ipm.getApplicationInfo(packageName, PackageManager.GET_META_DATA, userId);
+            if (appInfo == null) {
+                Log.w(TAG, "Null application info retrieved for package " + packageName
+                        + ", userId " + userId);
+                return null;
+            }
+
+            final UserHandle userHandle = new UserHandle(userId);
+            Drawable appIcon = mPackageManager.getApplicationIcon(appInfo);
+            Drawable icon = mPackageManager.getUserBadgedIcon(appIcon, userHandle);
+            CharSequence appLabel = mPackageManager.getApplicationLabel(appInfo);
+            CharSequence badgedAppLabel = mPackageManager.getUserBadgedLabel(appLabel, userHandle);
+            if (appLabel.toString().contentEquals(badgedAppLabel)) {
+                // If badged label is not different from original then no need for it as
+                // a separate content description.
+                badgedAppLabel = null;
+            }
+            request = new Request(packageName, userHandle, icon, appLabel, highBattery,
+                    badgedAppLabel);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error while retrieving application info for package " + packageName
+                    + ", userId " + userId, e);
+        }
+
+        return request;
+    }
+
+    public static class Request {
+        public final String packageName;
+        public final UserHandle userHandle;
+        public final Drawable icon;
+        public final CharSequence label;
+        public final boolean isHighBattery;
+        public final CharSequence contentDescription;
+
+        private Request(String packageName, UserHandle userHandle, Drawable icon,
+                CharSequence label, boolean isHighBattery, CharSequence contentDescription) {
+            this.packageName = packageName;
+            this.userHandle = userHandle;
+            this.icon = icon;
+            this.label = label;
+            this.isHighBattery = isHighBattery;
+            this.contentDescription = contentDescription;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 8729aa0..af30268 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -198,17 +198,12 @@
     public boolean launchFocusedTask() {
         if (mTaskStackView != null) {
             TaskStack stack = mTaskStackView.getStack();
-            // Iterate the stack views and try and find the focused task
-            List<TaskView> taskViews = mTaskStackView.getTaskViews();
-            int taskViewCount = taskViews.size();
-            for (int j = 0; j < taskViewCount; j++) {
-                TaskView tv = taskViews.get(j);
-                Task task = tv.getTask();
-                if (tv.isFocusedTask()) {
-                    onTaskViewClicked(mTaskStackView, tv, stack, task, false, false, null,
-                            INVALID_STACK_ID);
-                    return true;
-                }
+            Task task = mTaskStackView.getFocusedTask();
+            if (task != null) {
+                TaskView taskView = mTaskStackView.getChildViewForTask(task);
+                onTaskViewClicked(mTaskStackView, taskView, stack, task, false, false, null,
+                        INVALID_STACK_ID);
+                return true;
             }
         }
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 39c45cc..4a11b93 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -116,6 +116,7 @@
     List<TaskView> mImmutableTaskViews = new ArrayList<>();
     LayoutInflater mInflater;
     boolean mLayersDisabled;
+    boolean mTouchExplorationEnabled;
 
     Interpolator mFastOutSlowInInterpolator;
 
@@ -183,6 +184,8 @@
 
     @Override
     protected void onAttachedToWindow() {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
         EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
         super.onAttachedToWindow();
     }
@@ -425,7 +428,7 @@
                         visibleStackRange[1] <= taskIndex && taskIndex <= visibleStackRange[0]) {
                     mTmpTaskViewMap.put(task, tv);
                 } else {
-                    if (tv.isFocusedTask()) {
+                    if (mTouchExplorationEnabled && tv.isFocusedTask()) {
                         wasLastFocusedTaskAnimated = tv.isFocusAnimated();
                         lastFocusedTaskIndex = taskIndex;
                         resetFocusedTask();
@@ -716,6 +719,16 @@
         mFocusedTaskIndex = -1;
     }
 
+    /**
+     * Returns the focused task.
+     */
+    Task getFocusedTask() {
+        if (mFocusedTaskIndex != -1) {
+            return mStack.getTasks().get(mFocusedTaskIndex);
+        }
+        return null;
+    }
+
     @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4998b57..0cf58b1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -114,6 +114,7 @@
 import com.android.server.SystemServiceManager;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.am.ActivityStackSupervisor.ActivityDisplay;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.pm.Installer;
 import com.android.server.pm.UserManagerService;
@@ -134,6 +135,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
+import android.app.ActivityManager.TaskThumbnailInfo;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerInternal.SleepToken;
 import android.app.ActivityManagerNative;
@@ -4183,13 +4185,15 @@
                         "startActivityFromRecentsInner: Task " + taskId + " not found.");
             }
 
-            if (launchStackId != INVALID_STACK_ID && task.stack.mStackId != launchStackId) {
+            if (launchStackId != INVALID_STACK_ID) {
                 if (launchStackId == DOCKED_STACK_ID && bOptions != null) {
                     ActivityOptions activityOptions = new ActivityOptions(bOptions);
                     mWindowManager.setDockedStackCreateMode(activityOptions.getDockCreateMode());
                 }
-                mStackSupervisor.moveTaskToStackLocked(
-                        taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents");
+                if (task.stack.mStackId != launchStackId) {
+                    mStackSupervisor.moveTaskToStackLocked(
+                            taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents");
+                }
             }
 
             if (task.getRootActivity() != null) {
@@ -8575,8 +8579,16 @@
                     }
                 }
 
+                // Use the full screen as the context for the task thumbnail
+                final Point displaySize = new Point();
+                final TaskThumbnailInfo thumbnailInfo = new TaskThumbnailInfo();
+                r.task.stack.getDisplaySize(displaySize);
+                thumbnailInfo.taskWidth = displaySize.x;
+                thumbnailInfo.taskHeight = displaySize.y;
+                thumbnailInfo.screenOrientation = mConfiguration.orientation;
+
                 TaskRecord task = new TaskRecord(this, mStackSupervisor.getNextTaskId(), ainfo,
-                        intent, description);
+                        intent, description, thumbnailInfo);
 
                 int trimIdx = mRecentTasks.trimForTaskLocked(task, false);
                 if (trimIdx >= 0) {
@@ -8595,7 +8607,7 @@
                 mRecentTasks.add(task);
                 r.task.stack.addTask(task, false, false);
 
-                task.setLastThumbnail(thumbnail);
+                task.setLastThumbnailLocked(thumbnail);
                 task.freeLastThumbnail();
 
                 return task.taskId;
@@ -8690,12 +8702,25 @@
         Rect rect = new Rect();
         try {
             synchronized (this) {
-                TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
+                        taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
                 if (task == null) {
                     Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
                     return rect;
                 }
-                mWindowManager.getTaskBounds(task.taskId, rect);
+                if (task.stack != null) {
+                    // Return the bounds from window manager since it will be adjusted for various
+                    // things like the presense of a docked stack for tasks that aren't resizeable.
+                    mWindowManager.getTaskBounds(task.taskId, rect);
+                } else {
+                    // Task isn't in window manager yet since it isn't associated with a stack.
+                    // Return the persist value from activity manager
+                    if (task.mBounds != null) {
+                        rect.set(task.mBounds);
+                    } else if (task.mLastNonFullscreenBounds != null) {
+                        rect.set(task.mLastNonFullscreenBounds);
+                    }
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 9278a90..aa04bd7 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -932,7 +932,7 @@
         if (newThumbnail != null) {
             if (DEBUG_THUMBNAILS) Slog.i(TAG_THUMBNAILS,
                     "Setting thumbnail of " + this + " to " + newThumbnail);
-            boolean thumbnailUpdated = task.setLastThumbnail(newThumbnail);
+            boolean thumbnailUpdated = task.setLastThumbnailLocked(newThumbnail);
             if (thumbnailUpdated && isPersistable()) {
                 mStackSupervisor.mService.notifyTaskPersisterLocked(task, false);
             }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ea0db6b..400ebc6 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -31,6 +31,7 @@
 import static com.android.server.am.ActivityStackSupervisor.MOVING;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.ArraySet;
 
@@ -388,6 +389,10 @@
         mWindowManager.detachStack(mStackId);
     }
 
+    public void getDisplaySize(Point out) {
+        mActivityContainer.mActivityDisplay.mDisplay.getSize(out);
+    }
+
     void setBounds(Rect bounds) {
         mBounds = mFullscreen ? null : new Rect(bounds);
         if (mTaskPositioner != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index fb2c7b1..6acaa77 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3272,7 +3272,7 @@
             task.stack.removeTask(task, "restoreRecentTaskLocked", MOVING);
         }
 
-        ActivityStack stack =
+        final ActivityStack stack =
                 getStack(stackId, CREATE_IF_NEEDED, !ON_TOP);
 
         if (stack == null) {
@@ -3339,15 +3339,16 @@
             Slog.w(TAG, "moveTaskToStack: no task for id=" + taskId);
             return;
         }
-        if (StackId.preserveWindowOnTaskMove(stackId)) {
+
+        final ActivityRecord topActivity = task.getTopActivity();
+        if (StackId.preserveWindowOnTaskMove(stackId) && topActivity != null) {
             // We are about to relaunch the activity because its configuration changed due to
             // being maximized, i.e. size change. The activity will first remove the old window
             // and then add a new one. This call will tell window manager about this, so it can
             // preserve the old window until the new one is drawn. This prevents having a gap
             // between the removal and addition, in which no window is visible. We also want the
             // entrance of the new window to be properly animated.
-            ActivityRecord r = task.getTopActivity();
-            mWindowManager.setReplacingWindow(r.appToken, true /* animate */);
+            mWindowManager.setReplacingWindow(topActivity.appToken, true /* animate */);
         }
         final ActivityStack stack =
                 moveTaskToStackUncheckedLocked(task, stackId, toTop, forceFocus,
@@ -3680,7 +3681,7 @@
     void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
         r.mLaunchTaskBehind = false;
         final TaskRecord task = r.task;
-        task.setLastThumbnail(task.stack.screenshotActivities(r));
+        task.setLastThumbnailLocked(task.stack.screenshotActivities(r));
         mRecentTasks.addLocked(task);
         mService.notifyTaskStackChangedLocked();
         mWindowManager.setAppVisibility(r.appToken, false);
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 871331b..150baf0 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -16,22 +16,14 @@
 
 package com.android.server.am;
 
-import android.app.ActivityManager;
-import android.app.AppGlobals;
-import android.content.ComponentName;
 import android.content.pm.IPackageManager;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.os.Debug;
-import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.UserHandle;
-import android.text.format.DateUtils;
-import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.util.Xml;
 import android.os.Process;
 
@@ -51,14 +43,10 @@
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.Comparator;
-import java.util.List;
 
 import libcore.io.IoUtils;
 
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-
 public class TaskPersister {
     static final String TAG = "TaskPersister";
     static final boolean DEBUG = false;
@@ -104,6 +92,7 @@
     private static class WriteQueueItem {}
     private static class TaskWriteQueueItem extends WriteQueueItem {
         final TaskRecord mTask;
+
         TaskWriteQueueItem(TaskRecord task) {
             mTask = task;
         }
@@ -111,6 +100,7 @@
     private static class ImageWriteQueueItem extends WriteQueueItem {
         final String mFilename;
         Bitmap mImage;
+
         ImageWriteQueueItem(String filename, Bitmap image) {
             mFilename = filename;
             mImage = image;
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 031f6d2..22c3025 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
@@ -36,6 +37,8 @@
 import android.app.ActivityManager.StackId;
 import android.app.ActivityManager.TaskThumbnail;
 import android.app.ActivityManager.TaskDescription;
+import android.app.ActivityManager.TaskThumbnail;
+import android.app.ActivityManager.TaskThumbnailInfo;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
 import android.content.ComponentName;
@@ -46,6 +49,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Debug;
 import android.os.ParcelFileDescriptor;
@@ -193,6 +197,7 @@
     private Bitmap mLastThumbnail; // Last thumbnail captured for this item.
     private final File mLastThumbnailFile; // File containing last thumbnail.
     private final String mFilename;
+    private TaskThumbnailInfo mLastThumbnailInfo;
     CharSequence lastDescription; // Last description captured for this item.
 
     int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
@@ -233,6 +238,7 @@
         mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
                 TaskPersister.IMAGE_EXTENSION;
         mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
+        mLastThumbnailInfo = new TaskThumbnailInfo();
         taskId = _taskId;
         mAffiliatedTaskId = _taskId;
         voiceSession = _voiceSession;
@@ -246,11 +252,12 @@
     }
 
     TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
-            TaskDescription _taskDescription) {
+            TaskDescription _taskDescription, TaskThumbnailInfo thumbnailInfo) {
         mService = service;
         mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
                 TaskPersister.IMAGE_EXTENSION;
         mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
+        mLastThumbnailInfo = thumbnailInfo;
         taskId = _taskId;
         mAffiliatedTaskId = _taskId;
         voiceSession = null;
@@ -281,12 +288,14 @@
             int _effectiveUid, String _lastDescription, ArrayList<ActivityRecord> activities,
             long _firstActiveTime, long _lastActiveTime, long lastTimeMoved,
             boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
-            int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor,
-            int callingUid, String callingPackage, boolean resizeable, boolean privileged) {
+            TaskThumbnailInfo lastThumbnailInfo, int taskAffiliation, int prevTaskId,
+            int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
+            boolean resizeable, boolean privileged) {
         mService = service;
         mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
                 TaskPersister.IMAGE_EXTENSION;
         mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
+        mLastThumbnailInfo = lastThumbnailInfo;
         taskId = _taskId;
         intent = _intent;
         affinityIntent = _affinityIntent;
@@ -489,12 +498,40 @@
     }
 
     /**
-     * Sets the last thumbnail.
+     * Sets the last thumbnail with the current task bounds and the system orientation.
      * @return whether the thumbnail was set
      */
-    boolean setLastThumbnail(Bitmap thumbnail) {
+    boolean setLastThumbnailLocked(Bitmap thumbnail) {
+        final Configuration serviceConfig = mService.mConfiguration;
+        int taskWidth = 0;
+        int taskHeight = 0;
+        if (mBounds != null) {
+            // Non-fullscreen tasks
+            taskWidth = mBounds.width();
+            taskHeight = mBounds.height();
+        } else if (stack != null) {
+            // Fullscreen tasks
+            final Point displaySize = new Point();
+            stack.getDisplaySize(displaySize);
+            taskWidth = displaySize.x;
+            taskHeight = displaySize.y;
+        } else {
+            Slog.e(TAG, "setLastThumbnailLocked() called on Task without stack");
+        }
+        return setLastThumbnailLocked(thumbnail, taskWidth, taskHeight, serviceConfig.orientation);
+    }
+
+    /**
+     * Sets the last thumbnail with the current task bounds.
+     * @return whether the thumbnail was set
+     */
+    private boolean setLastThumbnailLocked(Bitmap thumbnail, int taskWidth, int taskHeight,
+            int screenOrientation) {
         if (mLastThumbnail != thumbnail) {
             mLastThumbnail = thumbnail;
+            mLastThumbnailInfo.taskWidth = taskWidth;
+            mLastThumbnailInfo.taskHeight = taskHeight;
+            mLastThumbnailInfo.screenOrientation = screenOrientation;
             if (thumbnail == null) {
                 if (mLastThumbnailFile != null) {
                     mLastThumbnailFile.delete();
@@ -509,6 +546,7 @@
 
     void getLastThumbnail(TaskThumbnail thumbs) {
         thumbs.mainThumbnail = mLastThumbnail;
+        thumbs.thumbnailInfo = mLastThumbnailInfo;
         thumbs.thumbnailFileDescriptor = null;
         if (mLastThumbnail == null) {
             thumbs.mainThumbnail = mService.mTaskPersister.getImageFromWriteQueue(mFilename);
@@ -523,12 +561,19 @@
         }
     }
 
+    /**
+     * Removes in-memory thumbnail data when the max number of in-memory task thumbnails is reached.
+     */
     void freeLastThumbnail() {
         mLastThumbnail = null;
     }
 
+    /**
+     * Removes all associated thumbnail data when a task is removed or pruned from recents.
+     */
     void disposeThumbnail() {
         mLastThumbnail = null;
+        mLastThumbnailInfo = null;
         lastDescription = null;
     }
 
@@ -778,7 +823,7 @@
             final ActivityRecord resumedActivity = stack.mResumedActivity;
             if (resumedActivity != null && resumedActivity.task == this) {
                 final Bitmap thumbnail = stack.screenshotActivities(resumedActivity);
-                setLastThumbnail(thumbnail);
+                setLastThumbnailLocked(thumbnail);
             }
         }
         final TaskThumbnail taskThumbnail = new TaskThumbnail();
@@ -990,6 +1035,7 @@
         if (lastTaskDescription != null) {
             lastTaskDescription.saveToXml(out);
         }
+        mLastThumbnailInfo.saveToXml(out);
         out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor));
         out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
         out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
@@ -1034,7 +1080,7 @@
             throws IOException, XmlPullParserException {
         Intent intent = null;
         Intent affinityIntent = null;
-        ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
+        ArrayList<ActivityRecord> activities = new ArrayList<>();
         ComponentName realActivity = null;
         ComponentName origActivity = null;
         String affinity = null;
@@ -1054,6 +1100,7 @@
         int taskId = INVALID_TASK_ID;
         final int outerDepth = in.getDepth();
         TaskDescription taskDescription = new TaskDescription();
+        TaskThumbnailInfo thumbnailInfo = new TaskThumbnailInfo();
         int taskAffiliation = INVALID_TASK_ID;
         int taskAffiliationColor = 0;
         int prevTaskId = INVALID_TASK_ID;
@@ -1102,6 +1149,8 @@
                 lastTimeOnTop = Long.valueOf(attrValue);
             } else if (ATTR_NEVERRELINQUISH.equals(attrName)) {
                 neverRelinquishIdentity = Boolean.valueOf(attrValue);
+            } else if (attrName.startsWith(TaskThumbnailInfo.ATTR_TASK_THUMBNAILINFO_PREFIX)) {
+                thumbnailInfo.restoreFromXml(attrName, attrValue);
             } else if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
                 taskDescription.restoreFromXml(attrName, attrValue);
             } else if (ATTR_TASK_AFFILIATION.equals(attrName)) {
@@ -1180,8 +1229,8 @@
                 affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
                 autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
                 activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
-                taskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
-                callingUid, callingPackage, resizeable, privileged);
+                taskDescription, thumbnailInfo, taskAffiliation, prevTaskId, nextTaskId,
+                taskAffiliationColor, callingUid, callingPackage, resizeable, privileged);
         task.updateOverrideConfiguration(bounds);
 
         for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
@@ -1366,6 +1415,8 @@
             sb.append(stringName);
             sb.append(" U=");
             sb.append(userId);
+            sb.append(" StackId=");
+            sb.append(stack != null ? stack.mStackId : INVALID_STACK_ID);
             sb.append(" sz=");
             sb.append(mActivities.size());
             sb.append('}');
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 25ef8e6..4852f02 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -164,6 +164,7 @@
     // Used for thumbnail transitions. True if we're scaling up, false if scaling down
     private boolean mNextAppTransitionScaleUp;
     private IRemoteCallback mNextAppTransitionCallback;
+    private IRemoteCallback mNextAppTransitionFutureCallback;
     private IRemoteCallback mAnimationFinishedCallback;
     private int mNextAppTransitionEnter;
     private int mNextAppTransitionExit;
@@ -1448,10 +1449,7 @@
             mNextAppTransitionAnimationsSpecs.clear();
             mNextAppTransitionAnimationsSpecsFuture = specsFuture;
             mNextAppTransitionScaleUp = scaleUp;
-            postAnimationCallback();
-            mNextAppTransitionCallback = callback;
-        } else {
-            postAnimationCallback();
+            mNextAppTransitionFutureCallback = callback;
         }
     }
 
@@ -1486,8 +1484,10 @@
                     }
                     synchronized (mServiceLock) {
                         mNextAppTransitionAnimationsSpecsPending = false;
-                        overridePendingAppTransitionMultiThumb(specs, mNextAppTransitionCallback,
-                                null /* finishedCallback */, mNextAppTransitionScaleUp);
+                        overridePendingAppTransitionMultiThumb(specs,
+                                mNextAppTransitionFutureCallback, null /* finishedCallback */,
+                                mNextAppTransitionScaleUp);
+                        mNextAppTransitionFutureCallback = null;
                         mWindowSurfacePlacer.requestTraversal();
                     }
                 }