diff options
author | Dianne Hackborn <hackbod@google.com> | 2014-08-20 15:25:13 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2014-08-20 18:03:43 -0700 |
commit | aec68bb89fe614181a20eb97340149406218ce2f (patch) | |
tree | b0e8807d7fb06487897a5813d9d8e3bb453f8175 | |
parent | cf038760f2af569a47e81d17d5cc86792a550c3e (diff) | |
download | frameworks_base-aec68bb89fe614181a20eb97340149406218ce2f.zip frameworks_base-aec68bb89fe614181a20eb97340149406218ce2f.tar.gz frameworks_base-aec68bb89fe614181a20eb97340149406218ce2f.tar.bz2 |
Fix issue #17038762: Add API to add entries to the recents list
New API Added to ActivityManager for adding an entry. See docs
there.
Repercussions:
- I hit a bug in system UI where if the thumbnail has alpha, it tries
to modify it, but thumbnails are loading immutable so crashes. Fixed
this by loading the bitmaps to be mutable.
- Improved dump output of recents; there was a lot of stuff missing.
Also split the recents dump output from the rest of the activity
output, since it can be really large.
- Added tests to the lovely ActivityTest app.
Bonus: new method on AppTask to control the exclude from recents flag.
Change-Id: I01e543db4d15320ee1701e95872fef73c116526c
-rw-r--r-- | api/current.txt | 4 | ||||
-rw-r--r-- | core/java/android/app/ActivityManager.java | 122 | ||||
-rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 50 | ||||
-rw-r--r-- | core/java/android/app/ApplicationPackageManager.java | 6 | ||||
-rw-r--r-- | core/java/android/app/IActivityManager.java | 6 | ||||
-rw-r--r-- | core/java/android/app/IAppTask.aidl | 1 | ||||
-rw-r--r-- | packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java | 10 | ||||
-rwxr-xr-x | services/core/java/com/android/server/am/ActivityManagerService.java | 208 | ||||
-rwxr-xr-x | services/core/java/com/android/server/am/ActivityStack.java | 4 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/TaskRecord.java | 54 | ||||
-rw-r--r-- | tests/ActivityTests/res/drawable-hdpi/icon.png | bin | 0 -> 6939 bytes | |||
-rw-r--r-- | tests/ActivityTests/res/drawable-mdpi/icon.png | bin | 0 -> 3744 bytes | |||
-rw-r--r-- | tests/ActivityTests/res/drawable-xhdpi/icon.png | bin | 0 -> 10942 bytes | |||
-rw-r--r-- | tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java | 58 |
14 files changed, 480 insertions, 43 deletions
diff --git a/api/current.txt b/api/current.txt index 5b8ba4d..29c9ffb 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3593,8 +3593,11 @@ package android.app { } public class ActivityManager { + method public int addAppTask(android.app.Activity, android.content.Intent, android.app.ActivityManager.TaskDescription, android.graphics.Bitmap); method public boolean clearApplicationUserData(); method public void dumpPackageState(java.io.FileDescriptor, java.lang.String); + method public int getAppTaskThumbnailHeight(); + method public int getAppTaskThumbnailWidth(); method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks(); method public android.content.pm.ConfigurationInfo getDeviceConfigurationInfo(); method public int getLargeMemoryClass(); @@ -3628,6 +3631,7 @@ package android.app { public static class ActivityManager.AppTask { method public void finishAndRemoveTask(); method public android.app.ActivityManager.RecentTaskInfo getTaskInfo(); + method public void setExcludeFromRecents(boolean); } public static class ActivityManager.MemoryInfo implements android.os.Parcelable { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 4b022ff..b86621f 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -16,6 +16,11 @@ package android.app; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Point; import android.os.BatteryStats; import android.os.IBinder; import android.os.ParcelFileDescriptor; @@ -47,16 +52,13 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.text.TextUtils; import android.util.DisplayMetrics; -import android.util.Log; import android.util.Slog; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * Interact with the overall activities running in the system. @@ -297,6 +299,8 @@ public class ActivityManager { /** @hide Process is being cached for later use and is empty. */ public static final int PROCESS_STATE_CACHED_EMPTY = 13; + Point mAppTaskThumbnailSize; + /*package*/ ActivityManager(Context context, Handler handler) { mContext = context; mHandler = handler; @@ -994,6 +998,103 @@ public class ActivityManager { } /** + * Return the current design width for {@link AppTask} thumbnails, for use + * with {@link #addAppTask}. + */ + public int getAppTaskThumbnailWidth() { + synchronized (this) { + ensureAppTaskThumbnailSizeLocked(); + return mAppTaskThumbnailSize.x; + } + } + + /** + * Return the current design height for {@link AppTask} thumbnails, for use + * with {@link #addAppTask}. + */ + public int getAppTaskThumbnailHeight() { + synchronized (this) { + ensureAppTaskThumbnailSizeLocked(); + return mAppTaskThumbnailSize.y; + } + } + + private void ensureAppTaskThumbnailSizeLocked() { + if (mAppTaskThumbnailSize == null) { + try { + mAppTaskThumbnailSize = ActivityManagerNative.getDefault().getAppTaskThumbnailSize(); + } catch (RemoteException e) { + throw new IllegalStateException("System dead?", e); + } + } + } + + /** + * Add a new {@link AppTask} for the calling application. This will create a new + * recents entry that is added to the <b>end</b> of all existing recents. + * + * @param activity The activity that is adding the entry. This is used to help determine + * the context that the new recents entry will be in. + * @param intent The Intent that describes the recents entry. This is the same Intent that + * you would have used to launch the activity for it. In generally you will want to set + * both {@link Intent#FLAG_ACTIVITY_NEW_DOCUMENT} and + * {@link Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS}; the latter is required since this recents + * entry will exist without an activity, so it doesn't make sense to not retain it when + * its activity disappears. The given Intent here also must have an explicit ComponentName + * set on it. + * @param description Optional additional description information. + * @param thumbnail Thumbnail to use for the recents entry. Should be the size given by + * {@link #getAppTaskThumbnailWidth()} and {@link #getAppTaskThumbnailHeight()}. If the + * bitmap is not that exact size, it will be recreated in your process, probably in a way + * you don't like, before the recents entry is added. + * + * @return Returns the task id of the newly added app task, or -1 if the add failed. The + * most likely cause of failure is that there is no more room for more tasks for your app. + */ + public int addAppTask(@NonNull Activity activity, @NonNull Intent intent, + @Nullable TaskDescription description, @NonNull Bitmap thumbnail) { + Point size; + synchronized (this) { + ensureAppTaskThumbnailSizeLocked(); + size = mAppTaskThumbnailSize; + } + final int tw = thumbnail.getWidth(); + final int th = thumbnail.getHeight(); + if (tw != size.x || th != size.y) { + Bitmap bm = Bitmap.createBitmap(size.x, size.y, thumbnail.getConfig()); + + // Use ScaleType.CENTER_CROP, except we leave the top edge at the top. + float scale; + float dx = 0, dy = 0; + if (tw * size.x > size.y * th) { + scale = (float) size.x / (float) th; + dx = (size.y - tw * scale) * 0.5f; + } else { + scale = (float) size.y / (float) tw; + dy = (size.x - th * scale) * 0.5f; + } + Matrix matrix = new Matrix(); + matrix.setScale(scale, scale); + matrix.postTranslate((int) (dx + 0.5f), 0); + + Canvas canvas = new Canvas(bm); + canvas.drawBitmap(thumbnail, matrix, null); + canvas.setBitmap(null); + + thumbnail = bm; + } + if (description == null) { + description = new TaskDescription(); + } + try { + return ActivityManagerNative.getDefault().addAppTask(activity.getActivityToken(), + intent, description, thumbnail); + } catch (RemoteException e) { + throw new IllegalStateException("System dead?", e); + } + } + + /** * Return a list of the tasks that are currently running, with * the most recent being first and older ones after in order. Note that * "running" does not mean any of the task's code is currently loaded or @@ -2514,5 +2615,20 @@ public class ActivityManager { return null; } } + + /** + * Modify the {@link Intent#FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS} flag in the root + * Intent of this AppTask. + * + * @param exclude If true, {@link Intent#FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS} will + * be set; otherwise, it will be cleared. + */ + public void setExcludeFromRecents(boolean exclude) { + try { + mAppTaskImpl.setExcludeFromRecents(exclude); + } catch (RemoteException e) { + Slog.e(TAG, "Invalid AppTask", e); + } + } } } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 1cb1047..3dafa4b 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -30,6 +30,8 @@ import android.content.pm.IPackageDataObserver; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Point; import android.graphics.Rect; import android.net.Uri; import android.os.Binder; @@ -564,6 +566,27 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case ADD_APP_TASK_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder activityToken = data.readStrongBinder(); + Intent intent = Intent.CREATOR.createFromParcel(data); + ActivityManager.TaskDescription descr + = ActivityManager.TaskDescription.CREATOR.createFromParcel(data); + Bitmap thumbnail = Bitmap.CREATOR.createFromParcel(data); + int res = addAppTask(activityToken, intent, descr, thumbnail); + reply.writeNoException(); + reply.writeInt(res); + return true; + } + + case GET_APP_TASK_THUMBNAIL_SIZE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + Point size = getAppTaskThumbnailSize(); + reply.writeNoException(); + size.writeToParcel(reply, 0); + return true; + } + case GET_TASKS_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int maxNum = data.readInt(); @@ -2877,6 +2900,33 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return list; } + public int addAppTask(IBinder activityToken, Intent intent, + ActivityManager.TaskDescription description, Bitmap thumbnail) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(activityToken); + intent.writeToParcel(data, 0); + description.writeToParcel(data, 0); + thumbnail.writeToParcel(data, 0); + mRemote.transact(ADD_APP_TASK_TRANSACTION, data, reply, 0); + reply.readException(); + int res = reply.readInt(); + data.recycle(); + reply.recycle(); + return res; + } + public Point getAppTaskThumbnailSize() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(GET_APP_TASK_THUMBNAIL_SIZE_TRANSACTION, data, reply, 0); + reply.readException(); + Point size = Point.CREATOR.createFromParcel(reply); + data.recycle(); + reply.recycle(); + return size; + } public List getTasks(int maxNum, int flags) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index b2812e3..9342ae5 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -749,10 +749,10 @@ final class ApplicationPackageManager extends PackageManager { putCachedIcon(name, dr); return dr; } catch (NameNotFoundException e) { - Log.w("PackageManager", "Failure retrieving resources for" + Log.w("PackageManager", "Failure retrieving resources for " + appInfo.packageName); } catch (Resources.NotFoundException e) { - Log.w("PackageManager", "Failure retrieving resources for" + Log.w("PackageManager", "Failure retrieving resources for " + appInfo.packageName + ": " + e.getMessage()); } catch (RuntimeException e) { // If an exception was thrown, fall through to return @@ -1100,7 +1100,7 @@ final class ApplicationPackageManager extends PackageManager { putCachedString(name, text); return text; } catch (NameNotFoundException e) { - Log.w("PackageManager", "Failure retrieving resources for" + Log.w("PackageManager", "Failure retrieving resources for " + appInfo.packageName); } catch (RuntimeException e) { // If an exception was thrown, fall through to return diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 8e21899..70514d8 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -36,6 +36,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.Point; import android.graphics.Rect; import android.net.Uri; import android.os.Bundle; @@ -119,6 +120,9 @@ public interface IActivityManager extends IInterface { public String getCallingPackage(IBinder token) throws RemoteException; public ComponentName getCallingActivity(IBinder token) throws RemoteException; public List<IAppTask> getAppTasks() throws RemoteException; + public int addAppTask(IBinder activityToken, Intent intent, + ActivityManager.TaskDescription description, Bitmap thumbnail) throws RemoteException; + public Point getAppTaskThumbnailSize() throws RemoteException; public List<RunningTaskInfo> getTasks(int maxNum, int flags) throws RemoteException; public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) throws RemoteException; @@ -765,4 +769,6 @@ public interface IActivityManager extends IInterface { int NOTIFY_ENTER_ANIMATION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+230; int KEYGUARD_WAITING_FOR_ACTIVITY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+231; int START_ACTIVITY_AS_CALLER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+232; + int ADD_APP_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+233; + int GET_APP_TASK_THUMBNAIL_SIZE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+234; } diff --git a/core/java/android/app/IAppTask.aidl b/core/java/android/app/IAppTask.aidl index 268b4dd..4e38c36 100644 --- a/core/java/android/app/IAppTask.aidl +++ b/core/java/android/app/IAppTask.aidl @@ -22,4 +22,5 @@ import android.app.ActivityManager; interface IAppTask { void finishAndRemoveTask(); ActivityManager.RecentTaskInfo getTaskInfo(); + void setExcludeFromRecents(boolean exclude); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index fb77751..5390daf 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -71,6 +71,8 @@ import java.util.Random; public class SystemServicesProxy { final static String TAG = "SystemServicesProxy"; + final static BitmapFactory.Options sBitmapOptions; + ActivityManager mAm; IActivityManager mIam; AppWidgetManager mAwm; @@ -89,6 +91,11 @@ public class SystemServicesProxy { Paint mBgProtectionPaint; Canvas mBgProtectionCanvas; + static { + sBitmapOptions = new BitmapFactory.Options(); + sBitmapOptions.inMutable = true; + } + /** Private constructor */ public SystemServicesProxy(Context context) { mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); @@ -258,7 +265,8 @@ public class SystemServicesProxy { Bitmap thumbnail = taskThumbnail.mainThumbnail; ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor; if (thumbnail == null && descriptor != null) { - thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor()); + thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(), + null, sBitmapOptions); } if (descriptor != null) { try { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b1d84f5..e8b1d03 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -40,6 +40,9 @@ import android.app.admin.DevicePolicyManager; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetManager; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Point; import android.graphics.Rect; import android.os.BatteryStats; import android.os.PersistableBundle; @@ -261,6 +264,7 @@ public final class ActivityManagerService extends ActivityManagerNative static final boolean DEBUG_VISBILITY = localLOGV || false; static final boolean DEBUG_PSS = localLOGV || false; static final boolean DEBUG_LOCKSCREEN = localLOGV || false; + static final boolean DEBUG_RECENTS = localLOGV || false; static final boolean VALIDATE_TOKENS = false; static final boolean SHOW_ACTIVITY_START_TIME = true; @@ -410,6 +414,21 @@ public final class ActivityManagerService extends ActivityManagerNative ArrayList<TaskRecord> mRecentTasks; ArraySet<TaskRecord> mTmpRecents = new ArraySet<TaskRecord>(); + /** + * For addAppTask: cached of the last activity component that was added. + */ + ComponentName mLastAddedTaskComponent; + + /** + * For addAppTask: cached of the last activity uid that was added. + */ + int mLastAddedTaskUid; + + /** + * For addAppTask: cached of the last ActivityInfo that was added. + */ + ActivityInfo mLastAddedTaskActivity; + public class PendingAssistExtras extends Binder implements Runnable { public final ActivityRecord activity; public boolean haveResult = false; @@ -1181,6 +1200,9 @@ public final class ActivityManagerService extends ActivityManagerNative /** Flag whether the device has a recents UI */ final boolean mHasRecents; + final int mThumbnailWidth; + final int mThumbnailHeight; + final ServiceThread mHandlerThread; final MainHandler mHandler; @@ -2229,8 +2251,10 @@ public final class ActivityManagerService extends ActivityManagerNative mConfigurationSeq = mConfiguration.seq = 1; mProcessCpuTracker.init(); - mHasRecents = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_hasRecents); + final Resources res = mContext.getResources(); + mHasRecents = res.getBoolean(com.android.internal.R.bool.config_hasRecents); + mThumbnailWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width); + mThumbnailHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height); mCompatModePackages = new CompatModePackages(this, systemDir, mHandler); mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler); @@ -3781,7 +3805,7 @@ public final class ActivityManagerService extends ActivityManagerNative } final void addRecentTaskLocked(TaskRecord task) { - int N = mRecentTasks.size(); + final int N = mRecentTasks.size(); // Quick case: check if the top-most recent task is the same. if (N > 0 && mRecentTasks.get(0) == task) { return; @@ -3790,10 +3814,25 @@ public final class ActivityManagerService extends ActivityManagerNative if (task.voiceSession != null) { return; } - // Remove any existing entries that are the same kind of task. + + trimRecentsForTask(task, true); + + if (N >= MAX_RECENT_TASKS) { + final TaskRecord tr = mRecentTasks.remove(N - 1); + tr.disposeThumbnail(); + tr.closeRecentsChain(); + } + mRecentTasks.add(0, task); + } + + /** + * If needed, remove oldest existing entries in recents that are for the same kind + * of task as the given one. + */ + int trimRecentsForTask(TaskRecord task, boolean doTrim) { + int N = mRecentTasks.size(); final Intent intent = task.intent; final boolean document = intent != null && intent.isDocument(); - final ComponentName comp = intent.getComponent(); int maxRecents = task.maxRecents - 1; for (int i=0; i<N; i++) { @@ -3825,6 +3864,12 @@ public final class ActivityManagerService extends ActivityManagerNative } } + if (!doTrim) { + // If the caller is not actually asking for a trim, just tell them we reached + // a point where the trim would happen. + return i; + } + // Either task and tr are the same or, their affinities match or their intents match // and neither of them is a document, or they are documents using the same activity // and their maxRecents has been reached. @@ -3842,12 +3887,8 @@ public final class ActivityManagerService extends ActivityManagerNative } notifyTaskPersisterLocked(tr, false); } - if (N >= MAX_RECENT_TASKS) { - final TaskRecord tr = mRecentTasks.remove(N - 1); - tr.disposeThumbnail(); - tr.closeRecentsChain(); - } - mRecentTasks.add(0, task); + + return -1; } @Override @@ -7639,7 +7680,10 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<N && maxNum > 0; i++) { TaskRecord tr = mRecentTasks.get(i); // Only add calling user or related users recent tasks - if (!includedUsers.contains(Integer.valueOf(tr.userId))) continue; + if (!includedUsers.contains(Integer.valueOf(tr.userId))) { + if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, not user: " + tr); + continue; + } // Return the entry if desired by the caller. We always return // the first entry, because callers always expect this to be the @@ -7656,11 +7700,14 @@ public final class ActivityManagerService extends ActivityManagerNative // If the caller doesn't have the GET_TASKS permission, then only // allow them to see a small subset of tasks -- their own and home. if (!tr.isHomeTask() && tr.creatorUid != callingUid) { + if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, not allowed: " + tr); continue; } } if (tr.autoRemoveRecents && tr.getTopActivity() == null) { // Don't include auto remove tasks that are finished or finishing. + if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, auto-remove without activity: " + + tr); continue; } @@ -7675,11 +7722,15 @@ public final class ActivityManagerService extends ActivityManagerNative if (rti.origActivity != null) { if (pm.getActivityInfo(rti.origActivity, 0, userId) == null) { + if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, unavail orig act: " + + tr); continue; } } else if (rti.baseIntent != null) { if (pm.queryIntentActivities(rti.baseIntent, null, 0, userId) == null) { + if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, unavail intent: " + + tr); continue; } } @@ -7721,6 +7772,97 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override + public int addAppTask(IBinder activityToken, Intent intent, + ActivityManager.TaskDescription description, Bitmap thumbnail) throws RemoteException { + final int callingUid = Binder.getCallingUid(); + final long callingIdent = Binder.clearCallingIdentity(); + + try { + synchronized (this) { + ActivityRecord r = ActivityRecord.isInStackLocked(activityToken); + if (r == null) { + throw new IllegalArgumentException("Activity does not exist; token=" + + activityToken); + } + ComponentName comp = intent.getComponent(); + if (comp == null) { + throw new IllegalArgumentException("Intent " + intent + + " must specify explicit component"); + } + if (thumbnail.getWidth() != mThumbnailWidth + || thumbnail.getHeight() != mThumbnailHeight) { + throw new IllegalArgumentException("Bad thumbnail size: got " + + thumbnail.getWidth() + "x" + thumbnail.getHeight() + ", require " + + mThumbnailWidth + "x" + mThumbnailHeight); + } + if (intent.getSelector() != null) { + intent.setSelector(null); + } + if (intent.getSourceBounds() != null) { + intent.setSourceBounds(null); + } + if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0) { + if ((intent.getFlags()&Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS) == 0) { + // The caller has added this as an auto-remove task... that makes no + // sense, so turn off auto-remove. + intent.addFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS); + } + } else if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { + // Must be a new task. + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + if (!comp.equals(mLastAddedTaskComponent) || callingUid != mLastAddedTaskUid) { + mLastAddedTaskActivity = null; + } + ActivityInfo ainfo = mLastAddedTaskActivity; + if (ainfo == null) { + ainfo = mLastAddedTaskActivity = AppGlobals.getPackageManager().getActivityInfo( + comp, 0, UserHandle.getUserId(callingUid)); + if (ainfo.applicationInfo.uid != callingUid) { + throw new SecurityException( + "Can't add task for another application: target uid=" + + ainfo.applicationInfo.uid + ", calling uid=" + callingUid); + } + } + + TaskRecord task = new TaskRecord(this, mStackSupervisor.getNextTaskId(), ainfo, + intent, description); + + int trimIdx = trimRecentsForTask(task, false); + if (trimIdx >= 0) { + // If this would have caused a trim, then we'll abort because that + // means it would be added at the end of the list but then just removed. + return -1; + } + + final int N = mRecentTasks.size(); + if (N >= (MAX_RECENT_TASKS-1)) { + final TaskRecord tr = mRecentTasks.remove(N - 1); + tr.disposeThumbnail(); + tr.closeRecentsChain(); + } + + mRecentTasks.add(task); + r.task.stack.addTask(task, false, false); + + task.setLastThumbnail(thumbnail); + task.freeLastThumbnail(); + + return task.taskId; + } + } finally { + Binder.restoreCallingIdentity(callingIdent); + } + } + + @Override + public Point getAppTaskThumbnailSize() { + synchronized (this) { + return new Point(mThumbnailWidth, mThumbnailHeight); + } + } + + @Override public void setTaskDescription(IBinder token, ActivityManager.TaskDescription td) { synchronized (this) { ActivityRecord r = ActivityRecord.isInStackLocked(token); @@ -18056,14 +18198,16 @@ public final class ActivityManagerService extends ActivityManagerNative mCallingUid = callingUid; } - @Override - public void finishAndRemoveTask() { - // Ensure that we are called from the same process that created this AppTask + private void checkCaller() { if (mCallingUid != Binder.getCallingUid()) { - Slog.w(TAG, "finishAndRemoveTask: caller " + mCallingUid + throw new SecurityException("Caller " + mCallingUid + " does not match caller of getAppTasks(): " + Binder.getCallingUid()); - return; } + } + + @Override + public void finishAndRemoveTask() { + checkCaller(); synchronized (ActivityManagerService.this) { long origId = Binder.clearCallingIdentity(); @@ -18085,12 +18229,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public ActivityManager.RecentTaskInfo getTaskInfo() { - // Ensure that we are called from the same process that created this AppTask - if (mCallingUid != Binder.getCallingUid()) { - Slog.w(TAG, "finishAndRemoveTask: caller " + mCallingUid - + " does not match caller of getAppTasks(): " + Binder.getCallingUid()); - return null; - } + checkCaller(); synchronized (ActivityManagerService.this) { long origId = Binder.clearCallingIdentity(); @@ -18105,5 +18244,28 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } } + + @Override + public void setExcludeFromRecents(boolean exclude) { + checkCaller(); + + synchronized (ActivityManagerService.this) { + long origId = Binder.clearCallingIdentity(); + try { + TaskRecord tr = recentTaskForIdLocked(mTaskId); + if (tr != null) { + Intent intent = tr.getBaseIntent(); + if (exclude) { + intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + } else { + intent.setFlags(intent.getFlags() + & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } } } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index e309a03..5ad84d4 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -355,8 +355,8 @@ final class ActivityStack { mCurrentUser = mService.mCurrentUserId; // Get the activity screenshot thumbnail dimensions Resources res = mService.mContext.getResources(); - mThumbnailWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width); - mThumbnailHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height); + mThumbnailWidth = mService.mThumbnailWidth; + mThumbnailHeight = mService.mThumbnailHeight; } /** diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index ccca657..48cb61a 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -167,6 +167,34 @@ final class TaskRecord { setIntent(_intent, info); } + TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent, + ActivityManager.TaskDescription _taskDescription) { + mService = service; + mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX + + TaskPersister.IMAGE_EXTENSION; + mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename); + taskId = _taskId; + mAffiliatedTaskId = _taskId; + voiceSession = null; + voiceInteractor = null; + mActivities = new ArrayList<ActivityRecord>(); + setIntent(_intent, info); + + taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE; + isPersistable = true; + mCallingUid = info.applicationInfo.uid; + mCallingPackage = info.packageName; + // Clamp to [1, 100]. + maxRecents = Math.min(Math.max(info.maxRecents, 1), 100); + + taskType = APPLICATION_ACTIVITY_TYPE; + mTaskToReturnTo = HOME_ACTIVITY_TYPE; + userId = UserHandle.getUserId(info.applicationInfo.uid); + lastTaskDescription = _taskDescription; + mCallingUid = info.applicationInfo.uid; + mCallingPackage = info.packageName; + } + TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent, String _affinity, ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset, boolean _autoRemoveRecents, boolean _askedCompatMode, @@ -765,7 +793,7 @@ final class TaskRecord { } void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { - Slog.i(TAG, "Saving task=" + this); + if (ActivityManagerService.DEBUG_RECENTS) Slog.i(TAG, "Saving task=" + this); out.attribute(null, ATTR_TASKID, String.valueOf(taskId)); if (realActivity != null) { @@ -948,18 +976,15 @@ final class TaskRecord { activities.get(activityNdx).task = task; } - Slog.i(TAG, "Restored task=" + task); + if (ActivityManagerService.DEBUG_RECENTS) Slog.d(TAG, "Restored task=" + task); return task; } void dump(PrintWriter pw, String prefix) { - if (rootWasReset || userId != 0 || numFullscreen != 0) { - pw.print(prefix); pw.print(" rootWasReset="); pw.print(rootWasReset); - pw.print(" userId="); pw.print(userId); - pw.print(" taskType="); pw.print(taskType); - pw.print(" numFullscreen="); pw.print(numFullscreen); - pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo); - } + pw.print(prefix); pw.print("userId="); pw.print(userId); + pw.print(" creatorUid="); pw.print(creatorUid); + pw.print(" mCallingUid="); pw.print(mCallingUid); + pw.print(" mCallingPackage="); pw.println(mCallingPackage); if (affinity != null) { pw.print(prefix); pw.print("affinity="); pw.println(affinity); } @@ -991,6 +1016,17 @@ final class TaskRecord { pw.print(prefix); pw.print("realActivity="); pw.println(realActivity.flattenToShortString()); } + if (autoRemoveRecents || taskType != 0 || mTaskToReturnTo != 0 || numFullscreen != 0) { + pw.print(prefix); pw.print("autoRemoveRecents="); pw.print(autoRemoveRecents); + pw.print(" numFullscreen="); pw.print(numFullscreen); + pw.print(" taskType="); pw.print(taskType); + pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo); + } + if (rootWasReset || mNeverRelinquishIdentity || mReuseTask) { + pw.print(prefix); pw.print("rootWasReset="); pw.print(rootWasReset); + pw.print(" mNeverRelinquishIdentity="); pw.print(mNeverRelinquishIdentity); + pw.print(" mReuseTask="); pw.println(mReuseTask); + } pw.print(prefix); pw.print("Activities="); pw.println(mActivities); if (!askedCompatMode) { pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode); diff --git a/tests/ActivityTests/res/drawable-hdpi/icon.png b/tests/ActivityTests/res/drawable-hdpi/icon.png Binary files differnew file mode 100644 index 0000000..2eab6f2 --- /dev/null +++ b/tests/ActivityTests/res/drawable-hdpi/icon.png diff --git a/tests/ActivityTests/res/drawable-mdpi/icon.png b/tests/ActivityTests/res/drawable-mdpi/icon.png Binary files differnew file mode 100644 index 0000000..63aec6f --- /dev/null +++ b/tests/ActivityTests/res/drawable-mdpi/icon.png diff --git a/tests/ActivityTests/res/drawable-xhdpi/icon.png b/tests/ActivityTests/res/drawable-xhdpi/icon.png Binary files differnew file mode 100644 index 0000000..8ea9c48 --- /dev/null +++ b/tests/ActivityTests/res/drawable-xhdpi/icon.png diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java index 9002125..7f3aa77 100644 --- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java +++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java @@ -28,6 +28,7 @@ import android.content.ComponentName; import android.content.ContentProviderClient; import android.content.Intent; import android.content.ServiceConnection; +import android.graphics.BitmapFactory; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -219,7 +220,7 @@ public class ActivityTestMain extends Activity { @Override public boolean onMenuItemClick(MenuItem item) { Intent intent = new Intent(ActivityTestMain.this, UserTarget.class); sendOrderedBroadcastAsUser(intent, new UserHandle(mSecondUser), null, - new BroadcastResultReceiver(), + new BroadcastResultReceiver(), null, Activity.RESULT_OK, null, null); return true; } @@ -291,6 +292,30 @@ public class ActivityTestMain extends Activity { return true; } }); + menu.add("Add App Recent").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + addAppRecents(1); + return true; + } + }); + menu.add("Add App 10x Recent").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + addAppRecents(10); + return true; + } + }); + menu.add("Exclude!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + setExclude(true); + return true; + } + }); + menu.add("Include!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + setExclude(false); + return true; + } + }); return true; } @@ -317,7 +342,36 @@ public class ActivityTestMain extends Activity { mConnections.clear(); } - private View scrollWrap(View view) { + void addAppRecents(int count) { + ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + intent.setComponent(new ComponentName(this, ActivityTestMain.class)); + for (int i=0; i<count; i++) { + ActivityManager.TaskDescription desc = new ActivityManager.TaskDescription(); + desc.setLabel("Added #" + i); + Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon); + if ((i&1) == 0) { + desc.setIcon(bitmap); + } + int taskId = am.addAppTask(this, intent, desc, bitmap); + Log.i(TAG, "Added new task id #" + taskId); + } + } + + void setExclude(boolean exclude) { + ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); + List<ActivityManager.AppTask> tasks = am.getAppTasks(); + int taskId = getTaskId(); + for (int i=0; i<tasks.size(); i++) { + ActivityManager.AppTask task = tasks.get(i); + if (task.getTaskInfo().id == taskId) { + task.setExcludeFromRecents(exclude); + } + } + } + private View scrollWrap(View view) { ScrollView scroller = new ScrollView(this); scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT, ScrollView.LayoutParams.MATCH_PARENT)); |