diff options
-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 5b8ba4d21930..29c9ffb6f75c 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 4b022ff30895..b86621f235f8 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 1cb1047fe44d..3dafa4bedd78 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 b2812e3099ca..9342ae5bedff 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 8e21899f30f5..70514d8f0c84 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 268b4ddbe8b9..4e38c36865cc 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 fb77751c7bf2..5390daf6db00 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 b1d84f58ea59..e8b1d037d707 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 e309a037a0c5..5ad84d47ed98 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 ccca657904e7..48cb61a3de37 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 000000000000..2eab6f218c72 --- /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 000000000000..63aec6fca419 --- /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 000000000000..8ea9c4899a31 --- /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 900212505101..7f3aa7762804 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)); |