diff options
author | Craig Mautner <cmautner@google.com> | 2014-07-01 12:38:52 -0700 |
---|---|---|
committer | Craig Mautner <cmautner@google.com> | 2014-07-02 16:31:46 -0700 |
commit | c0ffce5ddd6446f1d46a49cdfaeda4a2ce408e1d (patch) | |
tree | d35f14610cdd43912a963295aa6719ae596a1669 /services | |
parent | 51cb97096814352127aed69e5ac97013e9172038 (diff) | |
download | frameworks_base-c0ffce5ddd6446f1d46a49cdfaeda4a2ce408e1d.zip frameworks_base-c0ffce5ddd6446f1d46a49cdfaeda4a2ce408e1d.tar.gz frameworks_base-c0ffce5ddd6446f1d46a49cdfaeda4a2ce408e1d.tar.bz2 |
Use cached thumbnails in Recent tasks.
The thumbnail returned from ActivityManager.getTaskThumbnail() now
contains either a Bitmap or a ParcelFileDescriptor that points to
a file containing a compressed Bitmap. The Recent tasks list is
now responsible for all thumbnail Bitmap caching as the activity
manager keeps only the most recent 5. This also permits low memory
devices to have many more tasks in the Recent tasks list.
As part of this CL the concept of subtasks is removed eliminating
code supporting the TaskAccessInfo and IThumbnailRetriever classes.
Fixes bug 15828934.
Change-Id: I0fd0320a1a04e3c78d79357899b83a2fff97abf2
Diffstat (limited to 'services')
8 files changed, 176 insertions, 337 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 45367a8..56be936 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -270,7 +270,10 @@ public final class ActivityManagerService extends ActivityManagerNative static final boolean IS_USER_BUILD = "user".equals(Build.TYPE); // Maximum number of recent tasks that we can remember. - static final int MAX_RECENT_TASKS = ActivityManager.isLowRamDeviceStatic() ? 10 : 200; + static final int MAX_RECENT_TASKS = ActivityManager.isLowRamDeviceStatic() ? 100 : 200; + + // Maximum number recent bitmaps to keep in memory. + static final int MAX_RECENT_BITMAPS = 5; // Amount of time after a call to stopAppSwitches() during which we will // prevent further untrusted switches from happening. @@ -3601,6 +3604,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (task.userId != tr.userId) { continue; } + if (i > MAX_RECENT_BITMAPS) { + tr.freeLastThumbnail(); + } final Intent trIntent = tr.intent; if ((task.affinity == null || !task.affinity.equals(tr.affinity)) && (intent == null || !intent.filterEquals(trIntent))) { @@ -7348,26 +7354,13 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public ActivityManager.TaskThumbnails getTaskThumbnails(int id) { + public ActivityManager.TaskThumbnail getTaskThumbnail(int id) { synchronized (this) { enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, - "getTaskThumbnails()"); + "getTaskThumbnail()"); TaskRecord tr = recentTaskForIdLocked(id); if (tr != null) { - return tr.getTaskThumbnailsLocked(); - } - } - return null; - } - - @Override - public Bitmap getTaskTopThumbnail(int id) { - synchronized (this) { - enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, - "getTaskTopThumbnail()"); - TaskRecord tr = recentTaskForIdLocked(id); - if (tr != null) { - return tr.getTaskTopThumbnailLocked(); + return tr.getTaskThumbnailLocked(); } } return null; @@ -7384,24 +7377,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } - @Override - public boolean removeSubTask(int taskId, int subTaskIndex) { - synchronized (this) { - enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS, - "removeSubTask()"); - long ident = Binder.clearCallingIdentity(); - try { - TaskRecord tr = recentTaskForIdLocked(taskId); - if (tr != null) { - return tr.removeTaskActivitiesLocked(subTaskIndex, true) != null; - } - return false; - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - private void killUnneededProcessLocked(ProcessRecord pr, String reason) { if (!pr.killedByAm) { Slog.i(TAG, "Killing " + pr.toShortString() + " (adj " + pr.setAdj + "): " + reason); @@ -7473,7 +7448,7 @@ public final class ActivityManagerService extends ActivityManagerNative private boolean removeTaskByIdLocked(int taskId, int flags) { TaskRecord tr = recentTaskForIdLocked(taskId); if (tr != null) { - tr.removeTaskActivitiesLocked(-1, false); + tr.removeTaskActivitiesLocked(); cleanUpRemovedTaskLocked(tr, flags); if (tr.isPersistable) { notifyTaskPersisterLocked(tr, true); diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 287ad00..fd2a0b1 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -16,6 +16,7 @@ package com.android.server.am; +import android.app.ActivityManager; import android.app.ActivityManager.TaskDescription; import android.os.PersistableBundle; import android.os.Trace; @@ -113,7 +114,6 @@ final class ActivityRecord { int realTheme; // actual theme resource we will use, never 0. int windowFlags; // custom window flags for preview window. TaskRecord task; // the task this is in. - ThumbnailHolder thumbHolder; // where our thumbnails should go. long createTime = System.currentTimeMillis(); long displayStartTime; // when we started launching this activity long fullyDrawnStartTime; // when we started launching this activity @@ -262,13 +262,6 @@ final class ActivityRecord { pw.print(" forceNewConfig="); pw.println(forceNewConfig); pw.print(prefix); pw.print("mActivityType="); pw.println(activityTypeToString(mActivityType)); - pw.print(prefix); pw.print("thumbHolder: "); - pw.print(Integer.toHexString(System.identityHashCode(thumbHolder))); - if (thumbHolder != null) { - pw.print(" bm="); pw.print(thumbHolder.lastThumbnail); - pw.print(" desc="); pw.print(thumbHolder.lastDescription); - } - pw.println(); if (displayStartTime != 0 || startTime != 0) { pw.print(prefix); pw.print("displayStartTime="); if (displayStartTime == 0) pw.print("0"); @@ -497,7 +490,7 @@ final class ActivityRecord { } } - void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) { + void setTask(TaskRecord newTask, boolean isRoot) { if (task != null && task.removeActivity(this)) { if (task != newTask) { task.stack.removeTask(task); @@ -506,18 +499,7 @@ final class ActivityRecord { (newTask == null ? null : newTask.stack)); } } - if (newThumbHolder == null) { - newThumbHolder = newTask; - } task = newTask; - if (!isRoot && (intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { - // This is the start of a new sub-task. - if (thumbHolder == null) { - thumbHolder = new ThumbnailHolder(); - } - } else { - thumbHolder = newThumbHolder; - } } boolean changeWindowTranslucency(boolean toOpaque) { @@ -764,18 +746,15 @@ final class ActivityRecord { } void updateThumbnail(Bitmap newThumbnail, CharSequence description) { - if (thumbHolder != null) { - if (newThumbnail != null) { - if (ActivityManagerService.DEBUG_THUMBNAILS) Slog.i(ActivityManagerService.TAG, - "Setting thumbnail of " + this + " holder " + thumbHolder - + " to " + newThumbnail); - thumbHolder.lastThumbnail = newThumbnail; - if (isPersistable()) { - mStackSupervisor.mService.notifyTaskPersisterLocked(task, false); - } + if (newThumbnail != null) { + if (ActivityManagerService.DEBUG_THUMBNAILS) Slog.i(ActivityManagerService.TAG, + "Setting thumbnail of " + this + " to " + newThumbnail); + task.setLastThumbnail(newThumbnail); + if (isPersistable()) { + mStackSupervisor.mService.notifyTaskPersisterLocked(task, false); } - thumbHolder.lastDescription = description; } + task.lastDescription = description; } void startLaunchTickingLocked() { @@ -1051,6 +1030,11 @@ final class ActivityRecord { return null; } + private static String createImageFilename(ActivityRecord r) { + return String.valueOf(r.task.taskId) + ACTIVITY_ICON_SUFFIX + r.createTime + + TaskPersister.IMAGE_EXTENSION; + } + void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { out.attribute(null, ATTR_ID, String.valueOf(createTime)); out.attribute(null, ATTR_LAUNCHEDFROMUID, String.valueOf(launchedFromUid)); @@ -1064,8 +1048,7 @@ final class ActivityRecord { out.attribute(null, ATTR_USERID, String.valueOf(userId)); if (taskDescription != null) { - TaskPersister.saveTaskDescription(taskDescription, String.valueOf(task.taskId) + - ACTIVITY_ICON_SUFFIX + createTime, out); + task.saveTaskDescription(taskDescription, createImageFilename(this), out); } out.startTag(null, TAG_INTENT); @@ -1109,9 +1092,9 @@ final class ActivityRecord { componentSpecified = Boolean.valueOf(attrValue); } else if (ATTR_USERID.equals(attrName)) { userId = Integer.valueOf(attrValue); - } else if (TaskPersister.readTaskDescriptionAttribute(taskDescription, attrName, + } else if (TaskRecord.readTaskDescriptionAttribute(taskDescription, attrName, attrValue)) { - // Completed in TaskPersister.readTaskDescriptionAttribute() + // Completed in TaskRecord.readTaskDescriptionAttribute() } else { Log.d(TAG, "Unknown ActivityRecord attribute=" + attrName); } @@ -1157,8 +1140,7 @@ final class ActivityRecord { r.persistentState = persistentState; if (createTime >= 0) { - taskDescription.setIcon(TaskPersister.restoreImage(String.valueOf(taskId) + - ACTIVITY_ICON_SUFFIX + createTime)); + taskDescription.setIcon(TaskPersister.restoreImage(createImageFilename(r))); } r.taskDescription = taskDescription; r.createTime = createTime; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 1ecb43c..f2922c3 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -2063,7 +2063,6 @@ final class ActivityStack { // bottom of the activity stack. This also keeps it // correctly ordered with any activities we previously // moved. - final ThumbnailHolder newThumbHolder; final TaskRecord targetTask; final ActivityRecord bottom = !mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ? @@ -2074,20 +2073,16 @@ final class ActivityStack { // same task affinity as the one we are moving, // then merge it into the same task. targetTask = bottom.task; - newThumbHolder = bottom.thumbHolder == null ? targetTask : bottom.thumbHolder; if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target + " out to bottom task " + bottom.task); } else { targetTask = createTaskRecord(mStackSupervisor.getNextTaskId(), target.info, null, null, null, false); - newThumbHolder = targetTask; targetTask.affinityIntent = target.intent; if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target + " out to new task " + target.task); } - target.thumbHolder = newThumbHolder; - final int targetTaskId = targetTask.taskId; mWindowManager.setAppGroupId(target.appToken, targetTaskId); @@ -2099,7 +2094,6 @@ final class ActivityStack { continue; } - ThumbnailHolder curThumbHolder = p.thumbHolder; canMoveOptions = false; if (noOptions && topOptions == null) { topOptions = p.takeOptionsLocked(); @@ -2112,7 +2106,7 @@ final class ActivityStack { + " Callers=" + Debug.getCallers(4)); if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p + " out to target's task " + target.task); - p.setTask(targetTask, curThumbHolder, false); + p.setTask(targetTask, false); targetTask.addActivityAtBottom(p); mWindowManager.setAppGroupId(p.appToken, targetTaskId); @@ -2243,7 +2237,7 @@ final class ActivityStack { + start + "-" + i + " to task=" + task + ":" + taskInsertionPoint); for (int srcPos = start; srcPos >= i; --srcPos) { final ActivityRecord p = activities.get(srcPos); - p.setTask(task, null, false); + p.setTask(task, false); task.addActivityAtIndex(taskInsertionPoint, p); if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + p @@ -3626,8 +3620,8 @@ final class ActivityStack { ci.topActivity = top.intent.getComponent(); ci.lastActiveTime = task.lastActiveTime; - if (top.thumbHolder != null) { - ci.description = top.thumbHolder.lastDescription; + if (top.task != null) { + ci.description = top.task.lastDescription; } ci.numActivities = numActivities; ci.numRunning = numRunning; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index e7f5720..dbd3638 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1894,11 +1894,11 @@ public final class ActivityStackSupervisor implements DisplayListener { r.setTask(targetStack.createTaskRecord(getNextTaskId(), newTaskInfo != null ? newTaskInfo : r.info, newTaskIntent != null ? newTaskIntent : intent, - voiceSession, voiceInteractor, true), null, true); + voiceSession, voiceInteractor, true), true); if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " + r.task); } else { - r.setTask(reuseTask, reuseTask, true); + r.setTask(reuseTask, true); } if (!movedHome) { if ((launchFlags & @@ -1959,7 +1959,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // An existing activity is starting this new activity, so we want // to keep the new one in the same task as the one that is starting // it. - r.setTask(sourceTask, sourceRecord.thumbHolder, false); + r.setTask(sourceTask, false); if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in existing task " + r.task + " from source " + sourceRecord); @@ -1970,9 +1970,8 @@ public final class ActivityStackSupervisor implements DisplayListener { targetStack = adjustStackFocus(r, newTask); targetStack.moveToFront(); ActivityRecord prev = targetStack.topActivity(); - r.setTask(prev != null ? prev.task - : targetStack.createTaskRecord(getNextTaskId(), r.info, intent, null, null, true), - null, true); + r.setTask(prev != null ? prev.task : targetStack.createTaskRecord(getNextTaskId(), + r.info, intent, null, null, true), true); mWindowManager.moveTaskToTop(r.task.taskId); if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new guessed " + r.task); diff --git a/services/core/java/com/android/server/am/TaskAccessInfo.java b/services/core/java/com/android/server/am/TaskAccessInfo.java deleted file mode 100644 index 50aeec1..0000000 --- a/services/core/java/com/android/server/am/TaskAccessInfo.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2011 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 java.util.ArrayList; - -import android.app.ActivityManager.TaskThumbnails; - -final class TaskAccessInfo extends TaskThumbnails { - final static class SubTask { - ThumbnailHolder holder; - ActivityRecord activity; - int index; - } - - public ActivityRecord root; - public int rootIndex; - - public ArrayList<SubTask> subtasks; -} diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java index eee7e9e..1982d7e 100644 --- a/services/core/java/com/android/server/am/TaskPersister.java +++ b/services/core/java/com/android/server/am/TaskPersister.java @@ -40,6 +40,7 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.LinkedList; public class TaskPersister { static final String TAG = "TaskPersister"; @@ -53,15 +54,12 @@ public class TaskPersister { private static final String TASKS_DIRNAME = "recent_tasks"; private static final String TASK_EXTENSION = ".xml"; private static final String IMAGES_DIRNAME = "recent_images"; - private static final String IMAGE_EXTENSION = ".png"; + static final String IMAGE_EXTENSION = ".png"; private static final String TAG_TASK = "task"; - private static final String ATTR_TASKDESCRIPTIONLABEL = "task_description_label"; - private static final String ATTR_TASKDESCRIPTIONCOLOR = "task_description_color"; - - private static File sImagesDir; - private static File sTasksDir; + static File sImagesDir; + static File sTasksDir; private final ActivityManagerService mService; private final ActivityStackSupervisor mStackSupervisor; @@ -132,49 +130,8 @@ public class TaskPersister { return stringWriter; } - static void saveImage(Bitmap image, String filename) throws IOException { - if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename); - FileOutputStream imageFile = null; - try { - imageFile = new FileOutputStream(new File(sImagesDir, filename + IMAGE_EXTENSION)); - image.compress(Bitmap.CompressFormat.PNG, 100, imageFile); - } catch (Exception e) { - Slog.e(TAG, "saveImage: unable to save " + filename, e); - } finally { - if (imageFile != null) { - imageFile.close(); - } - } - } - - static void saveTaskDescription(ActivityManager.TaskDescription taskDescription, - String iconFilename, XmlSerializer out) throws IOException { - if (taskDescription != null) { - final String label = taskDescription.getLabel(); - if (label != null) { - out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, label); - } - final int colorPrimary = taskDescription.getPrimaryColor(); - if (colorPrimary != 0) { - out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR, Integer.toHexString(colorPrimary)); - } - final Bitmap icon = taskDescription.getIcon(); - if (icon != null) { - saveImage(icon, iconFilename); - } - } - } - - static boolean readTaskDescriptionAttribute(ActivityManager.TaskDescription taskDescription, - String attrName, String attrValue) { - if (ATTR_TASKDESCRIPTIONLABEL.equals(attrName)) { - taskDescription.setLabel(attrValue); - return true; - } else if (ATTR_TASKDESCRIPTIONCOLOR.equals(attrName)) { - taskDescription.setPrimaryColor((int) Long.parseLong(attrValue, 16)); - return true; - } - return false; + void saveImage(Bitmap image, String filename) { + mLazyTaskWriterThread.saveImage(image, filename); } private String fileToString(File file) { @@ -314,16 +271,33 @@ public class TaskPersister { static Bitmap restoreImage(String filename) { if (DEBUG) Slog.d(TAG, "restoreImage: restoring " + filename); - return BitmapFactory.decodeFile(sImagesDir + File.separator + filename + IMAGE_EXTENSION); + return BitmapFactory.decodeFile(sImagesDir + File.separator + filename); } private class LazyTaskWriterThread extends Thread { boolean mSlow = true; + LinkedList<BitmapQueueEntry> mSaveImagesQueue = new LinkedList<BitmapQueueEntry>(); LazyTaskWriterThread(String name) { super(name); } + class BitmapQueueEntry { + final Bitmap mImage; + final String mFilename; + BitmapQueueEntry(Bitmap image, String filename) { + mImage = image; + mFilename = filename; + } + } + + void saveImage(Bitmap image, String filename) { + synchronized (mSaveImagesQueue) { + mSaveImagesQueue.add(new BitmapQueueEntry(image, filename)); + } + TaskPersister.this.notify(null, false); + } + @Override public void run() { ArraySet<Integer> persistentTaskIds = new ArraySet<Integer>(); @@ -344,6 +318,32 @@ public class TaskPersister { } } + // Write out one bitmap that needs saving each time through. + BitmapQueueEntry entry; + synchronized (mSaveImagesQueue) { + entry = mSaveImagesQueue.poll(); + // Are there any more after this one? + mRecentsChanged |= !mSaveImagesQueue.isEmpty(); + } + if (entry != null) { + final String filename = entry.mFilename; + if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename); + FileOutputStream imageFile = null; + try { + imageFile = new FileOutputStream(new File(sImagesDir, filename)); + entry.mImage.compress(Bitmap.CompressFormat.PNG, 100, imageFile); + } catch (Exception e) { + Slog.e(TAG, "saveImage: unable to save " + filename, e); + } finally { + if (imageFile != null) { + try { + imageFile.close(); + } catch (IOException e) { + } + } + } + } + StringWriter stringWriter = null; TaskRecord task = null; synchronized(mService) { diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 3ef9494..d221f96 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -24,12 +24,13 @@ import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE; import android.app.Activity; import android.app.ActivityManager; +import android.app.ActivityManager.TaskThumbnail; import android.app.ActivityOptions; -import android.app.IThumbnailRetriever; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; import android.graphics.Bitmap; +import android.os.ParcelFileDescriptor; import android.os.UserHandle; import android.service.voice.IVoiceInteractionSession; import android.util.Slog; @@ -39,12 +40,12 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; -final class TaskRecord extends ThumbnailHolder { - private static final String TAG_TASK = "task"; +final class TaskRecord { private static final String ATTR_TASKID = "task_id"; private static final String TAG_INTENT = "intent"; private static final String TAG_AFFINITYINTENT = "affinity_intent"; @@ -60,6 +61,8 @@ final class TaskRecord extends ThumbnailHolder { private static final String ATTR_LASTDESCRIPTION = "last_description"; private static final String ATTR_LASTTIMEMOVED = "last_time_moved"; private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity"; + private static final String ATTR_TASKDESCRIPTIONLABEL = "task_description_label"; + private static final String ATTR_TASKDESCRIPTIONCOLOR = "task_description_color"; private static final String LAST_ACTIVITY_ICON_SUFFIX = "_last_activity_icon_"; private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail"; @@ -72,7 +75,6 @@ final class TaskRecord extends ThumbnailHolder { Intent affinityIntent; // Intent of affinity-moved activity that started this task. ComponentName origActivity; // The non-alias activity component of the intent. ComponentName realActivity; // The actual activity component that started the task. - int numActivities; // Current number of activities in this task. long lastActiveTime; // Last time this task was active, including sleep. boolean rootWasReset; // True if the intent at the root of the task had // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag. @@ -123,11 +125,18 @@ final class TaskRecord extends ThumbnailHolder { // do not want to delete the stack when the task goes empty. boolean mReuseTask = false; + private Bitmap mLastThumbnail; // Last thumbnail captured for this item. + private final File mLastThumbnailFile; // File containing last thubmnail. + private final String mFilename; + CharSequence lastDescription; // Last description captured for this item. + final ActivityManagerService mService; TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent, IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) { mService = service; + mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX + TaskPersister.IMAGE_EXTENSION; + mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename); taskId = _taskId; voiceSession = _voiceSession; voiceInteractor = _voiceInteractor; @@ -142,6 +151,8 @@ final class TaskRecord extends ThumbnailHolder { long lastTimeMoved, boolean neverRelinquishIdentity, ActivityManager.TaskDescription _lastTaskDescription) { mService = service; + mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX + TaskPersister.IMAGE_EXTENSION; + mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename); taskId = _taskId; intent = _intent; affinityIntent = _affinityIntent; @@ -240,16 +251,38 @@ final class TaskRecord extends ThumbnailHolder { return mTaskToReturnTo; } - void disposeThumbnail() { - super.disposeThumbnail(); - for (int i=mActivities.size()-1; i>=0; i--) { - ThumbnailHolder thumb = mActivities.get(i).thumbHolder; - if (thumb != this) { - thumb.disposeThumbnail(); + void setLastThumbnail(Bitmap thumbnail) { + mLastThumbnail = thumbnail; + if (thumbnail == null) { + if (mLastThumbnailFile != null) { + mLastThumbnailFile.delete(); + } + } else { + mService.mTaskPersister.saveImage(thumbnail, mFilename); + } + } + + void getLastThumbnail(TaskThumbnail thumbs) { + thumbs.mainThumbnail = mLastThumbnail; + thumbs.thumbnailFileDescriptor = null; + if (mLastThumbnailFile.exists()) { + try { + thumbs.thumbnailFileDescriptor = ParcelFileDescriptor.open(mLastThumbnailFile, + ParcelFileDescriptor.MODE_READ_ONLY); + } catch (IOException e) { } } } + void freeLastThumbnail() { + mLastThumbnail = null; + } + + void disposeThumbnail() { + mLastThumbnail = null; + lastDescription = null; + } + /** Returns the intent for the root activity for this task */ Intent getBaseIntent() { return intent != null ? intent : affinityIntent; @@ -470,63 +503,22 @@ final class TaskRecord extends ThumbnailHolder { return null; } - public ActivityManager.TaskThumbnails getTaskThumbnailsLocked() { - TaskAccessInfo info = getTaskAccessInfoLocked(); - final ActivityRecord resumedActivity = stack.mResumedActivity; - if (resumedActivity != null && resumedActivity.thumbHolder == this) { - info.mainThumbnail = stack.screenshotActivities(resumedActivity); - } - if (info.mainThumbnail == null) { - info.mainThumbnail = lastThumbnail; - } - return info; - } - - public Bitmap getTaskTopThumbnailLocked() { + public TaskThumbnail getTaskThumbnailLocked() { if (stack != null) { final ActivityRecord resumedActivity = stack.mResumedActivity; if (resumedActivity != null && resumedActivity.task == this) { - // This task is the current resumed task, we just need to take - // a screenshot of it and return that. - return stack.screenshotActivities(resumedActivity); + final Bitmap thumbnail = stack.screenshotActivities(resumedActivity); + setLastThumbnail(thumbnail); } } - // Return the information about the task, to figure out the top - // thumbnail to return. - TaskAccessInfo info = getTaskAccessInfoLocked(); - if (info.numSubThumbbails <= 0) { - return info.mainThumbnail != null ? info.mainThumbnail : lastThumbnail; - } - return info.subtasks.get(info.numSubThumbbails-1).holder.lastThumbnail; + final TaskThumbnail taskThumbnail = new TaskThumbnail(); + getLastThumbnail(taskThumbnail); + return taskThumbnail; } - public ActivityRecord removeTaskActivitiesLocked(int subTaskIndex, - boolean taskRequired) { - TaskAccessInfo info = getTaskAccessInfoLocked(); - if (info.root == null) { - if (taskRequired) { - Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId); - } - return null; - } - - if (subTaskIndex < 0) { - // Just remove the entire task. - performClearTaskAtIndexLocked(info.rootIndex); - return info.root; - } - - if (subTaskIndex >= info.subtasks.size()) { - if (taskRequired) { - Slog.w(TAG, "removeTaskLocked: unknown subTaskIndex " + subTaskIndex); - } - return null; - } - - // Remove all of this task's activities starting at the sub task. - TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex); - performClearTaskAtIndexLocked(subtask.index); - return subtask.activity; + public void removeTaskActivitiesLocked() { + // Just remove the entire task. + performClearTaskAtIndexLocked(0); } boolean isHomeTask() { @@ -541,68 +533,6 @@ final class TaskRecord extends ThumbnailHolder { return mTaskToReturnTo == HOME_ACTIVITY_TYPE || mTaskToReturnTo == RECENTS_ACTIVITY_TYPE; } - public TaskAccessInfo getTaskAccessInfoLocked() { - final TaskAccessInfo thumbs = new TaskAccessInfo(); - // How many different sub-thumbnails? - final int NA = mActivities.size(); - int j = 0; - ThumbnailHolder holder = null; - while (j < NA) { - ActivityRecord ar = mActivities.get(j); - if (!ar.finishing) { - thumbs.root = ar; - thumbs.rootIndex = j; - holder = ar.thumbHolder; - if (holder != null) { - thumbs.mainThumbnail = holder.lastThumbnail; - } - j++; - break; - } - j++; - } - - if (j >= NA) { - return thumbs; - } - - ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>(); - thumbs.subtasks = subtasks; - while (j < NA) { - ActivityRecord ar = mActivities.get(j); - j++; - if (ar.finishing) { - continue; - } - if (ar.thumbHolder != holder && holder != null) { - thumbs.numSubThumbbails++; - holder = ar.thumbHolder; - TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask(); - sub.holder = holder; - sub.activity = ar; - sub.index = j-1; - subtasks.add(sub); - } - } - if (thumbs.numSubThumbbails > 0) { - thumbs.retriever = new IThumbnailRetriever.Stub() { - @Override - public Bitmap getThumbnail(int index) { - if (index < 0 || index >= thumbs.subtasks.size()) { - return null; - } - TaskAccessInfo.SubTask sub = thumbs.subtasks.get(index); - ActivityRecord resumedActivity = stack.mResumedActivity; - if (resumedActivity != null && resumedActivity.thumbHolder == sub.holder) { - return stack.screenshotActivities(resumedActivity); - } - return sub.holder.lastThumbnail; - } - }; - } - return thumbs; - } - /** * Find the activity in the history stack within the given task. Returns * the index within the history at which it's found, or < 0 if not found. @@ -691,6 +621,36 @@ final class TaskRecord extends ThumbnailHolder { setIntent(r.intent, r.info); } + void saveTaskDescription(ActivityManager.TaskDescription taskDescription, + String iconFilename, XmlSerializer out) throws IOException { + if (taskDescription != null) { + final String label = taskDescription.getLabel(); + if (label != null) { + out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, label); + } + final int colorPrimary = taskDescription.getPrimaryColor(); + if (colorPrimary != 0) { + out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR, Integer.toHexString(colorPrimary)); + } + final Bitmap icon = taskDescription.getIcon(); + if (icon != null) { + mService.mTaskPersister.saveImage(icon, iconFilename); + } + } + } + + static boolean readTaskDescriptionAttribute(ActivityManager.TaskDescription taskDescription, + String attrName, String attrValue) { + if (ATTR_TASKDESCRIPTIONLABEL.equals(attrName)) { + taskDescription.setLabel(attrValue); + } else if (ATTR_TASKDESCRIPTIONCOLOR.equals(attrName)) { + taskDescription.setPrimaryColor((int) Long.parseLong(attrValue, 16)); + } else { + return false; + } + return true; + } + void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { Slog.i(TAG, "Saving task=" + this); @@ -716,7 +676,7 @@ final class TaskRecord extends ThumbnailHolder { } if (lastTaskDescription != null) { - TaskPersister.saveTaskDescription(lastTaskDescription, String.valueOf(taskId) + + saveTaskDescription(lastTaskDescription, String.valueOf(taskId) + LAST_ACTIVITY_ICON_SUFFIX + lastActiveTime, out); } @@ -744,11 +704,6 @@ final class TaskRecord extends ThumbnailHolder { r.saveToXml(out); out.endTag(null, TAG_ACTIVITY); } - - final Bitmap thumbnail = getTaskTopThumbnailLocked(); - if (thumbnail != null) { - TaskPersister.saveImage(thumbnail, String.valueOf(taskId) + TASK_THUMBNAIL_SUFFIX); - } } static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor) @@ -800,8 +755,7 @@ final class TaskRecord extends ThumbnailHolder { lastTimeOnTop = Long.valueOf(attrValue); } else if (ATTR_NEVERRELINQUISH.equals(attrName)) { neverRelinquishIdentity = Boolean.valueOf(attrValue); - } else if (TaskPersister.readTaskDescriptionAttribute(taskDescription, attrName, - attrValue)) { + } else if (readTaskDescriptionAttribute(taskDescription, attrName, attrValue)) { // Completed in TaskPersister.readTaskDescriptionAttribute() } else { Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName); @@ -836,7 +790,7 @@ final class TaskRecord extends ThumbnailHolder { if (lastActiveTime >= 0) { taskDescription.setIcon(TaskPersister.restoreImage(String.valueOf(taskId) + - LAST_ACTIVITY_ICON_SUFFIX + lastActiveTime)); + LAST_ACTIVITY_ICON_SUFFIX + lastActiveTime + TaskPersister.IMAGE_EXTENSION)); } final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent, @@ -845,12 +799,9 @@ final class TaskRecord extends ThumbnailHolder { lastTimeOnTop, neverRelinquishIdentity, taskDescription); for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) { - final ActivityRecord r = activities.get(activityNdx); - r.thumbHolder = r.task = task; + activities.get(activityNdx).task = task; } - task.lastThumbnail = TaskPersister.restoreImage(taskId + TASK_THUMBNAIL_SUFFIX); - Slog.i(TAG, "Restored task=" + task); return task; } @@ -898,7 +849,8 @@ final class TaskRecord extends ThumbnailHolder { if (!askedCompatMode) { pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode); } - pw.print(prefix); pw.print("lastThumbnail="); pw.print(lastThumbnail); + pw.print(prefix); pw.print("lastThumbnail="); pw.print(mLastThumbnail); + pw.print(" lastThumbnailFile="); pw.print(mLastThumbnailFile); pw.print(" lastDescription="); pw.println(lastDescription); pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible); pw.print(" lastActiveTime="); pw.print(lastActiveTime); diff --git a/services/core/java/com/android/server/am/ThumbnailHolder.java b/services/core/java/com/android/server/am/ThumbnailHolder.java deleted file mode 100644 index a6974f5..0000000 --- a/services/core/java/com/android/server/am/ThumbnailHolder.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2011 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.graphics.Bitmap; - -public class ThumbnailHolder { - Bitmap lastThumbnail; // Last thumbnail captured for this item. - CharSequence lastDescription; // Last description captured for this item. - - void disposeThumbnail() { - lastThumbnail = null; - lastDescription = null; - } -} |