summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2011-04-08 18:14:09 -0700
committerDianne Hackborn <hackbod@google.com>2011-04-08 18:16:21 -0700
commitf26fd99a7c2f554b0297760bb66336473c7db61f (patch)
tree292bc65e6d068857bda10f2f45727a7c88601b64 /services
parentbdf7b013f81b0b56a18cc9dd2fb987b56d595650 (diff)
downloadframeworks_base-f26fd99a7c2f554b0297760bb66336473c7db61f.zip
frameworks_base-f26fd99a7c2f554b0297760bb66336473c7db61f.tar.gz
frameworks_base-f26fd99a7c2f554b0297760bb66336473c7db61f.tar.bz2
Rework thumbnails in activity manager.
We now only keep a thumbnail for the task, not for each activity. However if you use FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, we will make a new secondary thumbnail for that series of activities. There is a new API for the app to get these secondary thumbnails. Also set a default thumbnail size for non-xlarge screens so we have thumbnails on phones. (We need some smarter code in the platform for computing the actual thumbnail dimensions of the current device). And add a test app to show recent tasks + thumbnails. Change-Id: Ic36759f6635522118a2cb7f156662229a610c492
Diffstat (limited to 'services')
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java96
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java77
-rw-r--r--services/java/com/android/server/am/ActivityStack.java42
-rw-r--r--services/java/com/android/server/am/TaskRecord.java4
-rw-r--r--services/java/com/android/server/am/ThumbnailHolder.java24
5 files changed, 181 insertions, 62 deletions
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 2e72068..ef509b1 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -44,6 +44,7 @@ import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
import android.app.IServiceConnection;
import android.app.IThumbnailReceiver;
+import android.app.IThumbnailRetriever;
import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationManager;
@@ -2603,8 +2604,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " finishing=" + r.finishing);
r.makeFinishing();
mMainStack.mHistory.remove(i);
-
- r.inHistory = false;
+ r.takeFromHistory();
mWindowManager.removeAppToken(r);
if (VALIDATE_TOKENS) {
mWindowManager.validateAppTokens(mMainStack.mHistory);
@@ -3808,16 +3808,7 @@ public final class ActivityManagerService extends ActivityManagerNative
r = (ActivityRecord)mMainStack.mHistory.get(index);
r.icicle = icicle;
r.haveState = true;
- if (thumbnail != null) {
- r.thumbnail = thumbnail;
- if (r.task != null) {
- r.task.lastThumbnail = r.thumbnail;
- }
- }
- r.description = description;
- if (r.task != null) {
- r.task.lastDescription = r.description;
- }
+ r.updateThumbnail(thumbnail, description);
r.stopped = true;
r.state = ActivityState.STOPPED;
if (!r.finishing) {
@@ -4851,7 +4842,6 @@ public final class ActivityManagerService extends ActivityManagerNative
ActivityRecord next =
pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
ActivityRecord top = null;
- CharSequence topDescription = null;
TaskRecord curTask = null;
int numActivities = 0;
int numRunning = 0;
@@ -4865,7 +4855,6 @@ public final class ActivityManagerService extends ActivityManagerNative
(top.state == ActivityState.INITIALIZING
&& top.task == r.task)) {
top = r;
- topDescription = r.description;
curTask = r.task;
numActivities = numRunning = 0;
}
@@ -4875,9 +4864,6 @@ public final class ActivityManagerService extends ActivityManagerNative
if (r.app != null && r.app.thread != null) {
numRunning++;
}
- if (topDescription == null) {
- topDescription = r.description;
- }
if (localLOGV) Slog.v(
TAG, r.intent.getComponent().flattenToShortString()
@@ -4892,13 +4878,15 @@ public final class ActivityManagerService extends ActivityManagerNative
ci.baseActivity = r.intent.getComponent();
ci.topActivity = top.intent.getComponent();
if (canReadFb) {
- if (top.thumbnail != null) {
- ci.thumbnail = top.thumbnail;
- } else if (top.state == ActivityState.RESUMED) {
+ if (top.state == ActivityState.RESUMED) {
ci.thumbnail = top.stack.screenshotActivities(top);
+ } else if (top.thumbHolder != null) {
+ ci.thumbnail = top.thumbHolder.lastThumbnail;
}
}
- ci.description = topDescription;
+ if (top.thumbHolder != null) {
+ ci.description = top.thumbHolder.lastDescription;
+ }
ci.numActivities = numActivities;
ci.numRunning = numRunning;
//System.out.println(
@@ -5015,7 +5003,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- public Bitmap getTaskThumbnail(int id) {
+ public ActivityManager.TaskThumbnails getTaskThumbnails(int id) {
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
"getTaskThumbnail()");
@@ -5024,11 +5012,61 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
if (tr.taskId == id) {
- if (resumed != null && resumed.task == tr) {
- return resumed.stack.screenshotActivities(resumed);
+ final ActivityManager.TaskThumbnails thumbs
+ = new ActivityManager.TaskThumbnails();
+ if (resumed != null && resumed.thumbHolder == tr) {
+ thumbs.mainThumbnail = resumed.stack.screenshotActivities(resumed);
} else {
- return tr.lastThumbnail;
+ thumbs.mainThumbnail = tr.lastThumbnail;
+ }
+ // How many different sub-thumbnails?
+ final int NA = mMainStack.mHistory.size();
+ int j = 0;
+ ThumbnailHolder holder = null;
+ while (j < NA) {
+ ActivityRecord ar = (ActivityRecord)mMainStack.mHistory.get(j);
+ j++;
+ if (ar.task == tr) {
+ holder = ar.thumbHolder;
+ break;
+ }
+ }
+ ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();
+ thumbs.otherThumbnails = bitmaps;
+ ActivityRecord lastActivity = null;
+ while (j < NA) {
+ ActivityRecord ar = (ActivityRecord)mMainStack.mHistory.get(j);
+ j++;
+ if (ar.task != tr) {
+ break;
+ }
+ lastActivity = ar;
+ if (ar.thumbHolder != holder && holder != null) {
+ thumbs.numSubThumbbails++;
+ holder = ar.thumbHolder;
+ bitmaps.add(holder.lastThumbnail);
+ }
+ }
+ if (lastActivity != null && bitmaps.size() > 0) {
+ if (resumed == lastActivity) {
+ Bitmap bm = lastActivity.stack.screenshotActivities(lastActivity);
+ if (bm != null) {
+ bitmaps.remove(bitmaps.size()-1);
+ bitmaps.add(bm);
+ }
+ }
+ }
+ if (thumbs.numSubThumbbails > 0) {
+ thumbs.retriever = new IThumbnailRetriever.Stub() {
+ public Bitmap getThumbnail(int index) {
+ if (index < 0 || index >= thumbs.otherThumbnails.size()) {
+ return null;
+ }
+ return thumbs.otherThumbnails.get(index);
+ }
+ };
}
+ return thumbs;
}
}
}
@@ -5254,9 +5292,9 @@ public final class ActivityManagerService extends ActivityManagerNative
}
r = (ActivityRecord)mMainStack.mHistory.get(index);
}
- if (thumbnail == null) {
- thumbnail = r.thumbnail;
- description = r.description;
+ if (thumbnail == null && r.thumbHolder != null) {
+ thumbnail = r.thumbHolder.lastThumbnail;
+ description = r.thumbHolder.lastDescription;
}
if (thumbnail == null && !always) {
// If there is no thumbnail, and this entry is not actually
@@ -8440,7 +8478,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final String[] args = new String[0];
for (int i=list.size()-1; i>=0; i--) {
final ActivityRecord r = (ActivityRecord)list.get(i);
- final boolean full = !brief && (complete || !r.inHistory);
+ final boolean full = !brief && (complete || !r.isInHistory());
if (needNL) {
pw.println(" ");
needNL = false;
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 8d8f303..2703481 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -47,7 +47,7 @@ import java.util.HashSet;
/**
* An entry in the history stack, representing an activity.
*/
-class ActivityRecord extends IApplicationToken.Stub {
+final class ActivityRecord extends IApplicationToken.Stub {
final ActivityManagerService service; // owner
final ActivityStack stack; // owner
final ActivityInfo info; // all about me
@@ -74,6 +74,7 @@ class ActivityRecord extends IApplicationToken.Stub {
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 launchTime; // when we starting launching this activity
long startTime; // last time this activity was started
long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity
@@ -86,9 +87,7 @@ class ActivityRecord extends IApplicationToken.Stub {
ArrayList newIntents; // any pending new intents for single-top mode
HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
UriPermissionOwner uriPermissions; // current special URI access perms.
- ProcessRecord app; // if non-null, hosting application
- Bitmap thumbnail; // icon representation of paused screen
- CharSequence description; // textual description of paused screen
+ ProcessRecord app; // if non-null, hosting application
ActivityState state; // current state we are in
Bundle icicle; // last saved activity state
boolean frontOfTask; // is this the root activity of its task?
@@ -100,7 +99,6 @@ class ActivityRecord extends IApplicationToken.Stub {
boolean configDestroy; // need to destroy due to config change?
int configChangeFlags; // which config values have changed
boolean keysPaused; // has key dispatching been paused for it?
- boolean inHistory; // are we in the history stack?
int launchMode; // the launch mode activity attribute.
boolean visible; // does this activity's window need to be shown?
boolean sleeping; // have we told the activity to sleep?
@@ -114,6 +112,8 @@ class ActivityRecord extends IApplicationToken.Stub {
String stringName; // for caching of toString().
+ private boolean inHistory; // are we in the history stack?
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("packageName="); pw.print(packageName);
pw.print(" processName="); pw.println(processName);
@@ -175,6 +175,7 @@ class ActivityRecord extends IApplicationToken.Stub {
pw.print(" launchMode="); pw.println(launchMode);
pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
pw.print(" thumbnailNeeded="); pw.println(thumbnailNeeded);
+ pw.print(prefix); pw.print("thumbHolder="); pw.println(thumbHolder);
if (launchTime != 0 || startTime != 0) {
pw.print(prefix); pw.print("launchTime=");
TimeUtils.formatDuration(launchTime, pw); pw.print(" startTime=");
@@ -328,10 +329,55 @@ class ActivityRecord extends IApplicationToken.Stub {
}
}
+ void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) {
+ if (inHistory && !finishing) {
+ if (task != null) {
+ task.numActivities--;
+ }
+ if (newTask != null) {
+ newTask.numActivities++;
+ }
+ }
+ 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;
+ }
+ }
+
+ void putInHistory() {
+ if (!inHistory) {
+ inHistory = true;
+ if (task != null && !finishing) {
+ task.numActivities++;
+ }
+ }
+ }
+
+ void takeFromHistory() {
+ if (inHistory) {
+ inHistory = false;
+ if (task != null && !finishing) {
+ task.numActivities--;
+ }
+ }
+ }
+
+ boolean isInHistory() {
+ return inHistory;
+ }
+
void makeFinishing() {
if (!finishing) {
finishing = true;
- if (task != null) {
+ if (task != null && inHistory) {
task.numActivities--;
}
}
@@ -430,6 +476,25 @@ class ActivityRecord extends IApplicationToken.Stub {
}
}
+ void updateThumbnail(Bitmap newThumbnail, CharSequence description) {
+ if ((intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+ // This is a logical break in the task; it repre
+ }
+ if (thumbHolder != null) {
+ if (newThumbnail != null) {
+ thumbHolder.lastThumbnail = newThumbnail;
+ }
+ thumbHolder.lastDescription = description;
+ }
+ }
+
+ void clearThumbnail() {
+ if (thumbHolder != null) {
+ thumbHolder.lastThumbnail = null;
+ thumbHolder.lastDescription = null;
+ }
+ }
+
// IApplicationToken
public boolean mayFreezeScreenLocked(ProcessRecord app) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index c087aec..e1d380b 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -69,7 +69,7 @@ import java.util.List;
/**
* State and management of a single stack of activities.
*/
-public class ActivityStack {
+final public class ActivityStack {
static final String TAG = ActivityManagerService.TAG;
static final boolean localLOGV = ActivityManagerService.localLOGV;
static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH;
@@ -789,10 +789,7 @@ public class ActivityStack {
mLastPausedActivity = prev;
prev.state = ActivityState.PAUSING;
prev.task.touchActiveTime();
- prev.thumbnail = screenshotActivities(prev);
- if (prev.task != null) {
- prev.task.lastThumbnail = prev.thumbnail;
- }
+ prev.updateThumbnail(screenshotActivities(prev), null);
mService.updateCpuStats();
@@ -986,7 +983,7 @@ public class ActivityStack {
mService.reportResumedActivityLocked(next);
}
- next.thumbnail = null;
+ next.clearThumbnail();
if (mMainStack) {
mService.setFocusedActivityLocked(next);
}
@@ -1513,8 +1510,7 @@ public class ActivityStack {
addPos = i+1;
if (!startIt) {
mHistory.add(addPos, r);
- r.inHistory = true;
- r.task.numActivities++;
+ r.putInHistory();
mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
r.info.screenOrientation, r.fullscreen);
if (VALIDATE_TOKENS) {
@@ -1546,9 +1542,8 @@ public class ActivityStack {
// Slot the activity into the history stack and proceed
mHistory.add(addPos, r);
- r.inHistory = true;
+ r.putInHistory();
r.frontOfTask = newTask;
- r.task.numActivities++;
if (NH > 0) {
// We want to show the starting preview window if we are
// switching to a new task, or the next activity's process is
@@ -1711,7 +1706,7 @@ public class ActivityStack {
// If the activity currently at the bottom has the
// same task affinity as the one we are moving,
// then merge it into the same task.
- target.task = p.task;
+ target.setTask(p.task, p.thumbHolder, false);
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ " out to bottom task " + p.task);
} else {
@@ -1719,7 +1714,8 @@ public class ActivityStack {
if (mService.mCurTask <= 0) {
mService.mCurTask = 1;
}
- target.task = new TaskRecord(mService.mCurTask, target.info, null);
+ target.setTask(new TaskRecord(mService.mCurTask, target.info, null),
+ null, false);
target.task.affinityIntent = target.intent;
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ " out to new task " + target.task);
@@ -1729,6 +1725,7 @@ public class ActivityStack {
replyChainEnd = targetI;
}
int dstPos = 0;
+ ThumbnailHolder curThumbHolder = target.thumbHolder;
for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
p = (ActivityRecord)mHistory.get(srcPos);
if (p.finishing) {
@@ -1736,9 +1733,8 @@ public class ActivityStack {
}
if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
+ " out to target's task " + target.task);
- task.numActivities--;
- p.task = target.task;
- target.task.numActivities++;
+ p.setTask(target.task, curThumbHolder, false);
+ curThumbHolder = p.thumbHolder;
mHistory.remove(srcPos);
mHistory.add(dstPos, p);
mService.mWindowManager.moveAppToken(dstPos, p);
@@ -1867,12 +1863,10 @@ public class ActivityStack {
lastReparentPos--;
}
mHistory.remove(srcPos);
- p.task.numActivities--;
- p.task = task;
+ p.setTask(task, null, false);
mHistory.add(lastReparentPos, p);
if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
+ " in to resetting task " + task);
- task.numActivities++;
mService.mWindowManager.moveAppToken(lastReparentPos, p);
mService.mWindowManager.setAppGroupId(p, p.task.taskId);
if (VALIDATE_TOKENS) {
@@ -2525,11 +2519,11 @@ public class ActivityStack {
if (mService.mCurTask <= 0) {
mService.mCurTask = 1;
}
- r.task = new TaskRecord(mService.mCurTask, r.info, intent);
+ r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in new task " + r.task);
} else {
- r.task = reuseTask;
+ r.setTask(reuseTask, reuseTask, true);
}
newTask = true;
moveHomeToFrontFromLaunchLocked(launchFlags);
@@ -2572,7 +2566,7 @@ public class ActivityStack {
// 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.task = sourceRecord.task;
+ r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in existing task " + r.task);
@@ -2583,9 +2577,9 @@ public class ActivityStack {
final int N = mHistory.size();
ActivityRecord prev =
N > 0 ? (ActivityRecord)mHistory.get(N-1) : null;
- r.task = prev != null
+ r.setTask(prev != null
? prev.task
- : new TaskRecord(mService.mCurTask, r.info, intent);
+ : new TaskRecord(mService.mCurTask, r.info, intent), null, true);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in new guessed " + r.task);
}
@@ -3403,7 +3397,7 @@ public class ActivityStack {
if (r.state != ActivityState.DESTROYED) {
r.makeFinishing();
mHistory.remove(r);
- r.inHistory = false;
+ r.takeFromHistory();
r.state = ActivityState.DESTROYED;
mService.mWindowManager.removeAppToken(r);
if (VALIDATE_TOKENS) {
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index 86cec42..e8c87e1 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -23,7 +23,7 @@ import android.graphics.Bitmap;
import java.io.PrintWriter;
-class TaskRecord {
+class TaskRecord extends ThumbnailHolder {
final int taskId; // Unique identifier for this task.
final String affinity; // The affinity name for this task, or null.
Intent intent; // The original intent that started the task.
@@ -34,8 +34,6 @@ class TaskRecord {
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.
- Bitmap lastThumbnail; // Last thumbnail captured for this task.
- CharSequence lastDescription; // Last description captured for this task.
String stringName; // caching of toString() result.
diff --git a/services/java/com/android/server/am/ThumbnailHolder.java b/services/java/com/android/server/am/ThumbnailHolder.java
new file mode 100644
index 0000000..02f4fcb
--- /dev/null
+++ b/services/java/com/android/server/am/ThumbnailHolder.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+}