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();
}
}