summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2010-12-08 11:25:25 -0800
committerAndroid (Google) Code Review <android-gerrit@google.com>2010-12-08 11:25:25 -0800
commit514d7d8d95faadcc860f3a462daafe5fe0be3cf4 (patch)
tree92f6afee777ab89d11ed04d53d0f764e6542ccb0
parentf9dee3b1d314365cb2bb5c6574d287d6a7c3d241 (diff)
parent0aae2d4e0075fd699cf40b26dca0eb2c3b3e37d2 (diff)
downloadframeworks_base-514d7d8d95faadcc860f3a462daafe5fe0be3cf4.zip
frameworks_base-514d7d8d95faadcc860f3a462daafe5fe0be3cf4.tar.gz
frameworks_base-514d7d8d95faadcc860f3a462daafe5fe0be3cf4.tar.bz2
Merge "Rework activity lifecycle so onSaveInstanceState() is after onPause()."
-rw-r--r--core/java/android/app/Activity.java54
-rw-r--r--core/java/android/app/ActivityManagerNative.java14
-rw-r--r--core/java/android/app/ActivityThread.java108
-rw-r--r--core/java/android/app/IActivityManager.java6
-rw-r--r--core/java/android/app/LocalActivityManager.java4
-rw-r--r--core/java/android/os/Build.java2
-rw-r--r--core/java/android/view/IWindowManager.aidl6
-rw-r--r--services/java/com/android/server/ScreenRotationAnimation.java37
-rw-r--r--services/java/com/android/server/WindowManagerService.java85
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java44
-rw-r--r--services/java/com/android/server/am/ActivityStack.java39
11 files changed, 270 insertions, 129 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a0a6b42..d679ef6 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -297,7 +297,7 @@ import java.util.List;
* <p>Followed by either <code>onResume()</code> if the activity
* returns back to the front, or <code>onStop()</code> if it becomes
* invisible to the user.</td>
- * <td align="center"><font color="#800000"><strong>Yes</strong></font></td>
+ * <td align="center"><font color="#800000"><strong>Pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB}</strong></font></td>
* <td align="center"><code>onResume()</code> or<br>
* <code>onStop()</code></td>
* </tr>
@@ -347,6 +347,14 @@ import java.util.List;
* because the later is not part of the lifecycle callbacks, so will not
* be called in every situation as described in its documentation.</p>
*
+ * <p class="note">Be aware that these semantics will change slightly between
+ * applications targeting platforms starting with {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+ * vs. those targeting prior platforms. Starting with Honeycomb, an application
+ * is not in the killable state until its {@link #onStop} has returned. This
+ * impacts when {@link #onSaveInstanceState(Bundle)} may be called (it may be
+ * safely called after {@link #onPause()} and allows and application to safely
+ * wait until {@link #onStop()} to save persistent state.</p>
+ *
* <p>For those methods that are not marked as being killable, the activity's
* process will not be killed by the system starting from the time the method
* is called and continuing after it returns. Thus an activity is in the killable
@@ -489,7 +497,7 @@ import java.util.List;
* paused. Note this implies
* that the user pressing BACK from your activity does <em>not</em>
* mean "cancel" -- it means to leave the activity with its current contents
- * saved away. Cancelling edits in an activity must be provided through
+ * saved away. Canceling edits in an activity must be provided through
* some other mechanism, such as an explicit "revert" or "undo" option.</p>
*
* <p>See the {@linkplain android.content.ContentProvider content package} for
@@ -1255,11 +1263,8 @@ public class Activity extends ContextThemeWrapper
* can use the given <var>canvas</var>, which is configured to draw into the
* bitmap, for rendering if desired.
*
- * <p>The default implementation renders the Screen's current view
- * hierarchy into the canvas to generate a thumbnail.
- *
- * <p>If you return false, the bitmap will be filled with a default
- * thumbnail.
+ * <p>The default implementation returns fails and does not draw a thumbnail;
+ * this will result in the platform creating its own thumbnail if needed.
*
* @param outBitmap The bitmap to contain the thumbnail.
* @param canvas Can be used to render into the bitmap.
@@ -1272,40 +1277,7 @@ public class Activity extends ContextThemeWrapper
* @see #onPause
*/
public boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas) {
- if (mDecor == null) {
- return false;
- }
-
- int paddingLeft = 0;
- int paddingRight = 0;
- int paddingTop = 0;
- int paddingBottom = 0;
-
- // Find System window and use padding so we ignore space reserved for decorations
- // like the status bar and such.
- final FrameLayout top = (FrameLayout) mDecor;
- for (int i = 0; i < top.getChildCount(); i++) {
- View child = top.getChildAt(i);
- if (child.isFitsSystemWindowsFlagSet()) {
- paddingLeft = child.getPaddingLeft();
- paddingRight = child.getPaddingRight();
- paddingTop = child.getPaddingTop();
- paddingBottom = child.getPaddingBottom();
- break;
- }
- }
-
- final int visibleWidth = mDecor.getWidth() - paddingLeft - paddingRight;
- final int visibleHeight = mDecor.getHeight() - paddingTop - paddingBottom;
-
- canvas.save();
- canvas.scale( (float) outBitmap.getWidth() / visibleWidth,
- (float) outBitmap.getHeight() / visibleHeight);
- canvas.translate(-paddingLeft, -paddingTop);
- mDecor.draw(canvas);
- canvas.restore();
-
- return true;
+ return false;
}
/**
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f3cc4ee..34788a5 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -359,8 +359,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
case ACTIVITY_PAUSED_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
- Bundle map = data.readBundle();
- activityPaused(token, map);
+ activityPaused(token);
reply.writeNoException();
return true;
}
@@ -368,10 +367,11 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
case ACTIVITY_STOPPED_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
+ Bundle map = data.readBundle();
Bitmap thumbnail = data.readInt() != 0
? Bitmap.CREATOR.createFromParcel(data) : null;
CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data);
- activityStopped(token, thumbnail, description);
+ activityStopped(token, map, thumbnail, description);
reply.writeNoException();
return true;
}
@@ -1688,25 +1688,25 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
reply.recycle();
}
- public void activityPaused(IBinder token, Bundle state) throws RemoteException
+ public void activityPaused(IBinder token) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
- data.writeBundle(state);
mRemote.transact(ACTIVITY_PAUSED_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
- public void activityStopped(IBinder token,
- Bitmap thumbnail, CharSequence description) throws RemoteException
+ public void activityStopped(IBinder token, Bundle state,
+ Bitmap thumbnail, CharSequence description) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
+ data.writeBundle(state);
if (thumbnail != null) {
data.writeInt(1);
thumbnail.writeToParcel(data, 0);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a8f08c2..45500bc 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -239,6 +239,14 @@ public final class ActivityThread {
nextIdle = null;
}
+ public boolean isPreHoneycomb() {
+ if (activity != null) {
+ return activity.getApplicationInfo().targetSdkVersion
+ < android.os.Build.VERSION_CODES.HONEYCOMB;
+ }
+ return false;
+ }
+
public String toString() {
ComponentName componentName = intent.getComponent();
return "ActivityRecord{"
@@ -2299,41 +2307,44 @@ public final class ActivityThread {
private int mThumbnailWidth = -1;
private int mThumbnailHeight = -1;
+ private Bitmap mAvailThumbnailBitmap = null;
+ private Canvas mThumbnailCanvas = null;
private final Bitmap createThumbnailBitmap(ActivityClientRecord r) {
- Bitmap thumbnail = null;
+ Bitmap thumbnail = mAvailThumbnailBitmap;
try {
- int w = mThumbnailWidth;
- int h;
- if (w < 0) {
- Resources res = r.activity.getResources();
- mThumbnailHeight = h =
- res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
-
- mThumbnailWidth = w =
- res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
- } else {
- h = mThumbnailHeight;
- }
-
- // On platforms where we don't want thumbnails, set dims to (0,0)
- if ((w > 0) && (h > 0)) {
- View topView = r.activity.getWindow().getDecorView();
-
- // Maximize bitmap by capturing in native aspect.
- if (topView.getWidth() >= topView.getHeight()) {
- thumbnail = Bitmap.createBitmap(w, h, THUMBNAIL_FORMAT);
+ if (thumbnail == null) {
+ int w = mThumbnailWidth;
+ int h;
+ if (w < 0) {
+ Resources res = r.activity.getResources();
+ mThumbnailHeight = h =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
+
+ mThumbnailWidth = w =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
} else {
- thumbnail = Bitmap.createBitmap(h, w, THUMBNAIL_FORMAT);
+ h = mThumbnailHeight;
}
- thumbnail.eraseColor(0);
- Canvas cv = new Canvas(thumbnail);
- if (!r.activity.onCreateThumbnail(thumbnail, cv)) {
- thumbnail = null;
+ // On platforms where we don't want thumbnails, set dims to (0,0)
+ if ((w > 0) && (h > 0)) {
+ thumbnail = Bitmap.createBitmap(w, h, THUMBNAIL_FORMAT);
+ thumbnail.eraseColor(0);
}
}
+ Canvas cv = mThumbnailCanvas;
+ if (cv == null) {
+ mThumbnailCanvas = cv = new Canvas();
+ }
+
+ cv.setBitmap(thumbnail);
+ if (!r.activity.onCreateThumbnail(thumbnail, cv)) {
+ mAvailThumbnailBitmap = thumbnail;
+ thumbnail = null;
+ }
+
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
@@ -2357,14 +2368,14 @@ public final class ActivityThread {
}
r.activity.mConfigChangeFlags |= configChanges;
- Bundle state = performPauseActivity(token, finished, true);
+ performPauseActivity(token, finished, r.isPreHoneycomb());
// Make sure any pending writes are now committed.
QueuedWork.waitToFinish();
// Tell the activity manager we have paused.
try {
- ActivityManagerNative.getDefault().activityPaused(token, state);
+ ActivityManagerNative.getDefault().activityPaused(token);
} catch (RemoteException ex) {
}
}
@@ -2404,6 +2415,8 @@ public final class ActivityThread {
state = new Bundle();
mInstrumentation.callActivityOnSaveInstanceState(r.activity, state);
r.state = state;
+ } else {
+ r.state = null;
}
// Now we are idle.
r.activity.mCalled = false;
@@ -2430,9 +2443,9 @@ public final class ActivityThread {
return state;
}
- final void performStopActivity(IBinder token) {
+ final void performStopActivity(IBinder token, boolean saveState) {
ActivityClientRecord r = mActivities.get(token);
- performStopActivityInner(r, null, false);
+ performStopActivityInner(r, null, false, saveState);
}
private static class StopInfo {
@@ -2447,9 +2460,18 @@ public final class ActivityThread {
}
}
+ /**
+ * Core implementation of stopping an activity. Note this is a little
+ * tricky because the server's meaning of stop is slightly different
+ * than our client -- for the server, stop means to save state and give
+ * it the result when it is done, but the window may still be visible.
+ * For the client, we want to call onStop()/onStart() to indicate when
+ * the activity's UI visibillity changes.
+ */
private final void performStopActivityInner(ActivityClientRecord r,
- StopInfo info, boolean keepShown) {
+ StopInfo info, boolean keepShown, boolean saveState) {
if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
+ Bundle state = null;
if (r != null) {
if (!keepShown && r.stopped) {
if (r.activity.mFinished) {
@@ -2479,6 +2501,17 @@ public final class ActivityThread {
}
}
+ // Next have the activity save its current state and managed dialogs...
+ if (!r.activity.mFinished && saveState) {
+ if (r.state == null) {
+ state = new Bundle();
+ mInstrumentation.callActivityOnSaveInstanceState(r.activity, state);
+ r.state = state;
+ } else {
+ state = r.state;
+ }
+ }
+
if (!keepShown) {
try {
// Now we are idle.
@@ -2530,7 +2563,7 @@ public final class ActivityThread {
r.activity.mConfigChangeFlags |= configChanges;
StopInfo info = new StopInfo();
- performStopActivityInner(r, info, show);
+ performStopActivityInner(r, info, show, true);
if (localLOGV) Slog.v(
TAG, "Finishing stop of " + r + ": show=" + show
@@ -2541,7 +2574,7 @@ public final class ActivityThread {
// Tell activity manager we have been stopped.
try {
ActivityManagerNative.getDefault().activityStopped(
- r.token, info.thumbnail, info.description);
+ r.token, r.state, info.thumbnail, info.description);
} catch (RemoteException ex) {
}
}
@@ -2557,7 +2590,7 @@ public final class ActivityThread {
private final void handleWindowVisibility(IBinder token, boolean show) {
ActivityClientRecord r = mActivities.get(token);
if (!show && !r.stopped) {
- performStopActivityInner(r, null, show);
+ performStopActivityInner(r, null, show, false);
} else if (show && r.stopped) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
@@ -2651,9 +2684,6 @@ public final class ActivityThread {
if (finishing) {
r.activity.mFinished = true;
}
- if (getNonConfigInstance) {
- r.activity.mChangingConfigurations = true;
- }
if (!r.paused) {
try {
r.activity.mCalled = false;
@@ -2924,9 +2954,11 @@ public final class ActivityThread {
r.onlyLocalRequest = tmp.onlyLocalRequest;
Intent currentIntent = r.activity.mIntent;
+ r.activity.mChangingConfigurations = true;
+
Bundle savedState = null;
if (!r.paused) {
- savedState = performPauseActivity(r.token, false, true);
+ savedState = performPauseActivity(r.token, false, r.isPreHoneycomb());
}
handleDestroyActivity(r.token, false, configChanges, true);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index dc18083..abffbdb 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -119,10 +119,10 @@ public interface IActivityManager extends IInterface {
public void attachApplication(IApplicationThread app) throws RemoteException;
/* oneway */
public void activityIdle(IBinder token, Configuration config) throws RemoteException;
- public void activityPaused(IBinder token, Bundle state) throws RemoteException;
+ public void activityPaused(IBinder token) throws RemoteException;
/* oneway */
- public void activityStopped(IBinder token,
- Bitmap thumbnail, CharSequence description) throws RemoteException;
+ public void activityStopped(IBinder token, Bundle state,
+ Bitmap thumbnail, CharSequence description) throws RemoteException;
/* oneway */
public void activityDestroyed(IBinder token) throws RemoteException;
public String getCallingPackage(IBinder token) throws RemoteException;
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 524de6f..5da04f1 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -176,7 +176,7 @@ public class LocalActivityManager {
}
if (desiredState == CREATED) {
if (localLOGV) Log.v(TAG, r.id + ": stopping");
- mActivityThread.performStopActivity(r);
+ mActivityThread.performStopActivity(r, false);
r.curState = CREATED;
}
return;
@@ -191,7 +191,7 @@ public class LocalActivityManager {
if (localLOGV) Log.v(TAG, r.id + ": pausing");
performPause(r, mFinishing);
if (localLOGV) Log.v(TAG, r.id + ": stopping");
- mActivityThread.performStopActivity(r);
+ mActivityThread.performStopActivity(r, false);
r.curState = CREATED;
}
return;
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index b4c6a2923..6e50e97 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -200,6 +200,8 @@ public class Build {
* <ul>
* <li> The default theme for applications is now dark holographic:
* {@link android.R.style#Theme_Holo}.
+ * <li> The activity lifecycle has changed slightly as per
+ * {@link android.app.Activity}.
* </ul>
*/
public static final int HONEYCOMB = CUR_DEVELOPMENT;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index beda099..0d6966f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -21,6 +21,7 @@ import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import android.content.res.Configuration;
+import android.graphics.Bitmap;
import android.view.IApplicationToken;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
@@ -182,4 +183,9 @@ interface IWindowManager
* @hide
*/
void thawRotation();
+
+ /**
+ * Create a screenshot of the applications currently displayed.
+ */
+ Bitmap screenshotApplications(int maxWidth, int maxHeight);
}
diff --git a/services/java/com/android/server/ScreenRotationAnimation.java b/services/java/com/android/server/ScreenRotationAnimation.java
index a95a6c7..ced7c7b 100644
--- a/services/java/com/android/server/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/ScreenRotationAnimation.java
@@ -152,31 +152,36 @@ class ScreenRotationAnimation {
}
}
- // Must be called while in a transaction.
- public void setRotation(int rotation) {
- mCurRotation = rotation;
-
- // Compute the transformation matrix that must be applied
- // to the snapshot to make it stay in the same original position
- // with the current screen rotation.
- int delta = deltaRotation(rotation, mSnapshotRotation);
- switch (delta) {
+ public static void createRotationMatrix(int rotation, int width, int height,
+ Matrix outMatrix) {
+ switch (rotation) {
case Surface.ROTATION_0:
- mSnapshotInitialMatrix.reset();
+ outMatrix.reset();
break;
case Surface.ROTATION_90:
- mSnapshotInitialMatrix.setRotate(90, 0, 0);
- mSnapshotInitialMatrix.postTranslate(mHeight, 0);
+ outMatrix.setRotate(90, 0, 0);
+ outMatrix.postTranslate(height, 0);
break;
case Surface.ROTATION_180:
- mSnapshotInitialMatrix.setRotate(180, 0, 0);
- mSnapshotInitialMatrix.postTranslate(mWidth, mHeight);
+ outMatrix.setRotate(180, 0, 0);
+ outMatrix.postTranslate(width, height);
break;
case Surface.ROTATION_270:
- mSnapshotInitialMatrix.setRotate(270, 0, 0);
- mSnapshotInitialMatrix.postTranslate(0, mWidth);
+ outMatrix.setRotate(270, 0, 0);
+ outMatrix.postTranslate(0, width);
break;
}
+ }
+
+ // Must be called while in a transaction.
+ public void setRotation(int rotation) {
+ mCurRotation = rotation;
+
+ // Compute the transformation matrix that must be applied
+ // to the snapshot to make it stay in the same original position
+ // with the current screen rotation.
+ int delta = deltaRotation(rotation, mSnapshotRotation);
+ createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
if (DEBUG) Slog.v(TAG, "**** ROTATION: " + delta);
setSnapshotTransform(mSnapshotInitialMatrix, 1.0f);
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index f78ebb9..7a2c0c6 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -60,6 +60,7 @@ import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
@@ -4922,6 +4923,90 @@ public class WindowManagerService extends IWindowManager.Stub
SystemProperties.set(StrictMode.VISUAL_PROPERTY, value);
}
+ public Bitmap screenshotApplications(int maxWidth, int maxHeight) {
+ if (!checkCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
+ "screenshotApplications()")) {
+ throw new SecurityException("Requires READ_FRAME_BUFFER permission");
+ }
+
+ Bitmap rawss;
+
+ final Rect frame = new Rect();
+
+ float scale;
+ int sw, sh, dw, dh;
+ int rot;
+
+ synchronized(mWindowMap) {
+ long ident = Binder.clearCallingIdentity();
+
+ int aboveAppLayer = mPolicy.windowTypeToLayerLw(
+ WindowManager.LayoutParams.TYPE_APPLICATION) * TYPE_LAYER_MULTIPLIER
+ + TYPE_LAYER_OFFSET;
+ aboveAppLayer += TYPE_LAYER_MULTIPLIER;
+
+ // Figure out the part of the screen that is actually the app.
+ for (int i=0; i<mWindows.size(); i++) {
+ WindowState ws = mWindows.get(i);
+ if (ws.mSurface == null) {
+ continue;
+ }
+ if (ws.mLayer >= aboveAppLayer) {
+ break;
+ }
+ final Rect wf = ws.mFrame;
+ final Rect cr = ws.mContentInsets;
+ int left = wf.left + cr.left;
+ int top = wf.top + cr.top;
+ int right = wf.right - cr.right;
+ int bottom = wf.bottom - cr.bottom;
+ frame.union(left, top, right, bottom);
+ }
+ Binder.restoreCallingIdentity(ident);
+
+ if (frame.isEmpty()) {
+ return null;
+ }
+
+ // The screenshot API does not apply the current screen rotation.
+ rot = mDisplay.getRotation();
+ int fw = frame.width();
+ int fh = frame.height();
+
+ // First try reducing to fit in x dimension.
+ scale = maxWidth/(float)fw;
+ sw = maxWidth;
+ sh = (int)(fh*scale);
+ if (sh > maxHeight) {
+ // y dimension became too long; constrain by that.
+ scale = maxHeight/(float)fh;
+ sw = (int)(fw*scale);
+ sh = maxHeight;
+ }
+
+ // The screen shot will contain the entire screen.
+ dw = (int)(mDisplay.getWidth()*scale);
+ dh = (int)(mDisplay.getHeight()*scale);
+ if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
+ int tmp = dw;
+ dw = dh;
+ dh = tmp;
+ rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
+ }
+ rawss = Surface.screenshot(dw, dh);
+ }
+
+ Bitmap bm = Bitmap.createBitmap(sw, sh, rawss.getConfig());
+ Matrix matrix = new Matrix();
+ ScreenRotationAnimation.createRotationMatrix(rot, dw, dh, matrix);
+ matrix.postTranslate(-(int)(frame.left*scale), -(int)(frame.top*scale));
+ Canvas canvas = new Canvas(bm);
+ canvas.drawBitmap(rawss, matrix, null);
+
+ rawss.recycle();
+ return bm;
+ }
+
public void freezeRotation() {
if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
"setRotation()")) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 1a10cff..1bfdcae 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3780,22 +3780,22 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- public final void activityPaused(IBinder token, Bundle icicle) {
- // Refuse possible leaked file descriptors
- if (icicle != null && icicle.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Bundle");
- }
-
+ public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
- mMainStack.activityPaused(token, icicle, false);
+ mMainStack.activityPaused(token, false);
Binder.restoreCallingIdentity(origId);
}
- public final void activityStopped(IBinder token, Bitmap thumbnail,
+ public final void activityStopped(IBinder token, Bundle icicle, Bitmap thumbnail,
CharSequence description) {
if (localLOGV) Slog.v(
TAG, "Activity stopped: token=" + token);
+ // Refuse possible leaked file descriptors
+ if (icicle != null && icicle.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Bundle");
+ }
+
ActivityRecord r = null;
final long origId = Binder.clearCallingIdentity();
@@ -3804,7 +3804,11 @@ public final class ActivityManagerService extends ActivityManagerNative
int index = mMainStack.indexOfTokenLocked(token);
if (index >= 0) {
r = (ActivityRecord)mMainStack.mHistory.get(index);
- r.thumbnail = thumbnail;
+ r.icicle = icicle;
+ r.haveState = true;
+ if (thumbnail != null) {
+ r.thumbnail = thumbnail;
+ }
r.description = description;
r.stopped = true;
r.state = ActivityState.STOPPED;
@@ -4822,6 +4826,10 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg);
}
+ final boolean canReadFb = checkCallingPermission(
+ android.Manifest.permission.READ_FRAME_BUFFER)
+ == PackageManager.PERMISSION_GRANTED;
+
int pos = mMainStack.mHistory.size()-1;
ActivityRecord next =
pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
@@ -4866,7 +4874,13 @@ public final class ActivityManagerService extends ActivityManagerNative
ci.id = curTask.taskId;
ci.baseActivity = r.intent.getComponent();
ci.topActivity = top.intent.getComponent();
- ci.thumbnail = top.thumbnail;
+ if (canReadFb) {
+ if (top.thumbnail != null) {
+ ci.thumbnail = top.thumbnail;
+ } else if (top.state == ActivityState.RESUMED) {
+ ci.thumbnail = top.stack.screenshotActivities();
+ }
+ }
ci.description = topDescription;
ci.numActivities = numActivities;
ci.numRunning = numRunning;
@@ -11806,7 +11820,7 @@ public final class ActivityManagerService extends ActivityManagerNative
} else if (app == mHeavyWeightProcess) {
// We don't want to kill the current heavy-weight process.
adj = HEAVY_WEIGHT_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
app.adjType = "heavy";
} else if (app == mHomeProcess) {
// This process is hosting what we currently consider to be the
@@ -11822,13 +11836,19 @@ public final class ActivityManagerService extends ActivityManagerNative
app.adjType = "bg-activities";
N = app.activities.size();
for (int j=0; j<N; j++) {
- if (app.activities.get(j).visible) {
+ ActivityRecord r = app.activities.get(j);
+ if (r.visible) {
// This app has a visible activity!
app.hidden = false;
adj = VISIBLE_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "visible";
break;
+ } else if (r.state == ActivityState.PAUSING
+ || r.state == ActivityState.PAUSED
+ || r.state == ActivityState.STOPPING) {
+ adj = PERCEPTIBLE_APP_ADJ;
+ app.adjType = "stopping";
}
}
} else {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index b4ea036..aa4cd66 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -46,6 +46,8 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -237,6 +239,9 @@ public class ActivityStack {
long mInitialStartTime = 0;
+ int mThumbnailWidth = -1;
+ int mThumbnailHeight = -1;
+
static final int PAUSE_TIMEOUT_MSG = 9;
static final int IDLE_TIMEOUT_MSG = 10;
static final int IDLE_NOW_MSG = 11;
@@ -256,7 +261,7 @@ public class ActivityStack {
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
Slog.w(TAG, "Activity pause timeout for " + token);
- activityPaused(token, null, true);
+ activityPaused(token, true);
} break;
case IDLE_TIMEOUT_MSG: {
if (mService.mDidDexOpt) {
@@ -564,8 +569,6 @@ public class ActivityStack {
// As part of the process of launching, ActivityThread also performs
// a resume.
r.state = ActivityState.RESUMED;
- r.icicle = null;
- r.haveState = false;
r.stopped = false;
mResumedActivity = r;
r.task.touchActiveTime();
@@ -580,6 +583,9 @@ public class ActivityStack {
r.stopped = true;
}
+ r.icicle = null;
+ r.haveState = false;
+
// Launch the new version setup screen if needed. We do this -after-
// launching the initial activity (that is, home), so that it can have
// a chance to initialize itself while in the background, making the
@@ -644,6 +650,23 @@ public class ActivityStack {
}
}
+ public final Bitmap screenshotActivities() {
+ Resources res = mService.mContext.getResources();
+ int w = mThumbnailWidth;
+ int h = mThumbnailHeight;
+ if (w < 0) {
+ mThumbnailWidth = w =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+ mThumbnailHeight = h =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
+ }
+
+ if (w > 0) {
+ return mService.mWindowManager.screenshotApplications(w, h);
+ }
+ return null;
+ }
+
private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
if (mPausingActivity != null) {
RuntimeException e = new RuntimeException();
@@ -663,6 +686,7 @@ public class ActivityStack {
mLastPausedActivity = prev;
prev.state = ActivityState.PAUSING;
prev.task.touchActiveTime();
+ prev.thumbnail = screenshotActivities();
mService.updateCpuStats();
@@ -726,10 +750,9 @@ public class ActivityStack {
}
}
- final void activityPaused(IBinder token, Bundle icicle, boolean timeout) {
+ final void activityPaused(IBinder token, boolean timeout) {
if (DEBUG_PAUSE) Slog.v(
- TAG, "Activity paused: token=" + token + ", icicle=" + icicle
- + ", timeout=" + timeout);
+ TAG, "Activity paused: token=" + token + ", timeout=" + timeout);
ActivityRecord r = null;
@@ -737,10 +760,6 @@ public class ActivityStack {
int index = indexOfTokenLocked(token);
if (index >= 0) {
r = (ActivityRecord)mHistory.get(index);
- if (!timeout) {
- r.icicle = icicle;
- r.haveState = true;
- }
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
if (mPausingActivity == r) {
r.state = ActivityState.PAUSED;