diff options
| author | Dianne Hackborn <hackbod@google.com> | 2010-12-07 23:51:29 -0800 |
|---|---|---|
| committer | Dianne Hackborn <hackbod@google.com> | 2010-12-08 00:35:27 -0800 |
| commit | 0aae2d4e0075fd699cf40b26dca0eb2c3b3e37d2 (patch) | |
| tree | 04686805aa8c15025416aac0da1efc95149385a0 | |
| parent | 0b38aa0f971f58ac96ebb331463ba2087af7b724 (diff) | |
| download | frameworks_base-0aae2d4e0075fd699cf40b26dca0eb2c3b3e37d2.zip frameworks_base-0aae2d4e0075fd699cf40b26dca0eb2c3b3e37d2.tar.gz frameworks_base-0aae2d4e0075fd699cf40b26dca0eb2c3b3e37d2.tar.bz2 | |
Rework activity lifecycle so onSaveInstanceState() is after onPause().
The goal is to fix a bunch of fragment-related bugs caused by various
things trying to do fragment transactions after onPause()... which
currently throws an exception, since this is after the activity's state
has been saved so the new fragment state can be lost.
The basic change is relatively simple -- we now consider processes
hosting paused or stopping activities to be unkillable, and the client
code now does the onSaveInstanceState() as part of stopping the
activity.
For compatibility, if an app's targetSdkVersion is < HONEYCOMB, the
client side will still call onSaveInstanceState() prior to onPause()
and just hold on to that state until it needs to report it in once
being stopped.
Also included here is a change to generate thumbnails by taking
screenshots. The code for generating thumbnails by re-rendering
the view hierarchy is thus removed.
Change-Id: Iac1191646bd3cadbfe65779297795f22edf7e74a
| -rw-r--r-- | core/java/android/app/Activity.java | 54 | ||||
| -rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 14 | ||||
| -rw-r--r-- | core/java/android/app/ActivityThread.java | 108 | ||||
| -rw-r--r-- | core/java/android/app/IActivityManager.java | 6 | ||||
| -rw-r--r-- | core/java/android/app/LocalActivityManager.java | 4 | ||||
| -rw-r--r-- | core/java/android/os/Build.java | 2 | ||||
| -rw-r--r-- | core/java/android/view/IWindowManager.aidl | 6 | ||||
| -rw-r--r-- | services/java/com/android/server/ScreenRotationAnimation.java | 37 | ||||
| -rw-r--r-- | services/java/com/android/server/WindowManagerService.java | 85 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 44 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActivityStack.java | 39 |
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; |
