diff options
| author | Dianne Hackborn <hackbod@google.com> | 2010-12-08 11:25:25 -0800 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-12-08 11:25:25 -0800 |
| commit | 514d7d8d95faadcc860f3a462daafe5fe0be3cf4 (patch) | |
| tree | 92f6afee777ab89d11ed04d53d0f764e6542ccb0 | |
| parent | f9dee3b1d314365cb2bb5c6574d287d6a7c3d241 (diff) | |
| parent | 0aae2d4e0075fd699cf40b26dca0eb2c3b3e37d2 (diff) | |
| download | frameworks_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.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; |
