From 02886a82d876aa5e31a92444fec70208599c509c Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Tue, 6 Dec 2016 09:10:06 -0800 Subject: [PATCH] Initial implementation of snapshots All this functionality is hidden behind a flag. If this flag is active, we disable the regular screenshots. Instead, we take a screenshot when an app transition for which a task is disappearing is starting. The screenshot gets stored into a gralloc buffer. SystemUI uses a new method to retrieve a snapshot gralloc buffer and then draws it using GraphicBuffer. createHardwareBitmap(). When starting an existing activity in an existing tasks, or when bringing an existing tasks to front from recents, we add a new snapshot starting window. For that, we reuse the existing starting window, but when creating the window, we use a fake window that draws the contents of the starting window. Test: runtest frameworks-services -c com.android.server.wm.TaskSnapshotControllerTest Bug: 31339431 Change-Id: If72df07b3e56f30413db5029d0887b8c9665aaf4 --- core/java/android/app/ActivityManager.java | 10 + core/java/android/app/IActivityManager.aidl | 6 + core/java/android/view/WindowManager.java | 11 + core/java/android/view/WindowManagerPolicy.java | 32 +- .../systemui/recents/misc/SystemServicesProxy.java | 59 +++- .../systemui/recents/views/TaskViewThumbnail.java | 3 +- .../android/server/am/ActivityManagerService.java | 388 +++++++++++---------- .../java/com/android/server/am/ActivityRecord.java | 31 +- .../java/com/android/server/am/ActivityStack.java | 24 +- .../com/android/server/am/ActivityStarter.java | 9 +- .../java/com/android/server/am/TaskRecord.java | 8 + .../android/server/policy/PhoneWindowManager.java | 22 +- .../android/server/policy/SplashScreenSurface.java | 24 +- .../server/wm/AppWindowContainerController.java | 126 ++++--- .../java/com/android/server/wm/AppWindowToken.java | 4 +- .../java/com/android/server/wm/DisplayContent.java | 27 +- .../android/server/wm/SnapshotStartingData.java | 42 +++ .../server/wm/SplashScreenStartingData.java | 60 ++++ .../java/com/android/server/wm/StartingData.java | 41 ++- .../com/android/server/wm/TaskSnapshotCache.java | 39 +++ .../android/server/wm/TaskSnapshotController.java | 132 +++++++ .../com/android/server/wm/TaskSnapshotSurface.java | 202 +++++++++++ .../server/wm/TaskWindowContainerController.java | 14 + .../android/server/wm/WindowManagerService.java | 29 +- .../java/com/android/server/wm/WindowState.java | 100 +++--- .../com/android/server/wm/WindowSurfacePlacer.java | 17 +- .../server/wm/TaskSnapshotControllerTest.java | 71 ++++ .../android/server/wm/TestWindowManagerPolicy.java | 5 - .../src/com/android/server/wm/WindowTestsBase.java | 6 + .../src/android/view/IWindowManagerImpl.java | 1 + 30 files changed, 1125 insertions(+), 418 deletions(-) create mode 100644 services/core/java/com/android/server/wm/SnapshotStartingData.java create mode 100644 services/core/java/com/android/server/wm/SplashScreenStartingData.java create mode 100644 services/core/java/com/android/server/wm/TaskSnapshotCache.java create mode 100644 services/core/java/com/android/server/wm/TaskSnapshotController.java create mode 100644 services/core/java/com/android/server/wm/TaskSnapshotSurface.java create mode 100644 services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 90373043efc8..2f8089d561aa 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -125,6 +125,16 @@ public class ActivityManager { private static volatile boolean sSystemReady = false; + /** + * System property to enable task snapshots. + * @hide + */ + public final static boolean ENABLE_TASK_SNAPSHOTS; + + static { + ENABLE_TASK_SNAPSHOTS = SystemProperties.getBoolean("persist.enable_task_snapshots", false); + } + static final class UidObserver extends IUidObserver.Stub { final OnUidImportanceListener mListener; diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 2ed9eab46277..a4c80b86b581 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -53,6 +53,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.GraphicBuffer; import android.graphics.Point; import android.graphics.Rect; import android.net.Uri; @@ -596,6 +597,11 @@ interface IActivityManager { /** Cancels the thumbnail transitions for the given task. */ void cancelTaskThumbnailTransition(int taskId); + /** + * @return a graphic buffer representing a screenshot of a task + */ + GraphicBuffer getTaskSnapshot(int taskId); + // WARNING: when these transactions are updated, check if they are any callers on the native // side. If so, make sure they are using the correct transaction ids and arguments. // If a transaction which will also be used on the native side is being inserted, add it diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index aa7631de55a7..aac3c18cbf87 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1319,6 +1319,17 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE = 0x00040000; /** + * Flag to indicate that this window is used as a task snapshot window. A task snapshot + * window is a starting window that gets shown with a screenshot from the previous state + * that is active until the app has drawn its first frame. + * + *

If this flag is set, SystemUI flags are ignored such that the real window behind can + * set the SystemUI flags. + * @hide + */ + public static final int PRIVATE_FLAG_TASK_SNAPSHOT = 0x00080000; + + /** * Control flags that are private to the platform. * @hide */ diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index dd852567360f..374813458c90 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -439,10 +439,15 @@ public interface WindowManagerPolicy { /** * Holds the contents of a starting window. {@link #addSplashScreen} needs to wrap the * contents of the starting window into an class implementing this interface, which then will be - * held by WM and passed into {@link #removeSplashScreen} when the starting window is no - * longer needed. + * held by WM and released with {@link #remove} when no longer needed. */ interface StartingSurface { + + /** + * Removes the starting window surface. Do not hold the window manager lock when calling + * this method! + */ + void remove(); } /** @@ -746,35 +751,14 @@ public interface WindowManagerPolicy { * @param overrideConfig override configuration to consider when generating * context to for resources. * - * @return Optionally you can return the View that was used to create the - * window, for easy removal in removeSplashScreen. + * @return The starting surface. * - * @see #removeSplashScreen */ public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, Configuration overrideConfig); /** - * Called when the first window of an application has been displayed, while - * {@link #addSplashScreen} has created a temporary initial window for - * that application. You should at this point remove the window from the - * window manager. This is called without the window manager locked so - * that you can call back into it. - * - *

Note: due to the nature of these functions not being called with the - * window manager locked, you must be prepared for this function to be - * called multiple times and/or an initial time with a null View window - * even if you previously returned one. - * - * @param appToken Token of the application that has started. - * @param surface Surface that was returned by {@link #addSplashScreen}. - * - * @see #addSplashScreen - */ - public void removeSplashScreen(IBinder appToken, StartingSurface surface); - - /** * Prepare for a window being added to the window manager. You can throw an * exception here to prevent the window being added, or do whatever setup * you need to keep track of the window. 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 779113216530..08970e41d006 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -22,13 +22,15 @@ import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; import android.app.ActivityManager; +import android.app.ActivityManager.TaskThumbnailInfo; import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.IActivityManager; -import android.app.ITaskStackListener; +import android.app.KeyguardManager; import android.app.UiModeManager; import android.content.ComponentName; import android.content.ContentResolver; @@ -45,6 +47,7 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.GraphicBuffer; import android.graphics.Paint; import android.graphics.Point; import android.graphics.PorterDuff; @@ -74,13 +77,13 @@ import android.view.WindowManager; import android.view.WindowManager.KeyboardShortcutsReceiver; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; -import android.app.KeyguardManager; import com.android.internal.app.AssistUtils; import com.android.internal.os.BackgroundThread; import com.android.systemui.R; import com.android.systemui.pip.tv.PipMenuActivity; import com.android.systemui.pip.tv.PipOnboardingActivity; +import com.android.systemui.recents.Constants; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsDebugFlags; import com.android.systemui.recents.RecentsImpl; @@ -603,7 +606,7 @@ public class SystemServicesProxy { } getThumbnail(taskId, thumbnailData); - if (thumbnailData.thumbnail != null) { + if (thumbnailData.thumbnail != null && !ActivityManager.ENABLE_TASK_SNAPSHOTS) { thumbnailData.thumbnail.setHasAlpha(false); // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top // left pixel, then assume the whole thumbnail is transparent. Generally, proper @@ -627,25 +630,43 @@ public class SystemServicesProxy { return; } - ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId); - if (taskThumbnail == null) { - return; - } - - Bitmap thumbnail = taskThumbnail.mainThumbnail; - ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor; - if (thumbnail == null && descriptor != null) { - thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(), - null, sBitmapOptions); - } - if (descriptor != null) { + if (ActivityManager.ENABLE_TASK_SNAPSHOTS) { + GraphicBuffer graphicBuffer = null; try { - descriptor.close(); - } catch (IOException e) { + graphicBuffer = ActivityManager.getService().getTaskSnapshot(taskId); + } catch (RemoteException e) { + Log.w(TAG, "Failed to retrieve snapshot", e); + } + if (graphicBuffer != null) { + thumbnailDataOut.thumbnail = Bitmap.createHardwareBitmap(graphicBuffer); + } else { + thumbnailDataOut.thumbnail = null; + } + + // TODO: Retrieve screen orientation. + thumbnailDataOut.thumbnailInfo = new TaskThumbnailInfo(); + thumbnailDataOut.thumbnailInfo.screenOrientation = ORIENTATION_PORTRAIT; + } else { + ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId); + if (taskThumbnail == null) { + return; + } + + Bitmap thumbnail = taskThumbnail.mainThumbnail; + ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor; + if (thumbnail == null && descriptor != null) { + thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(), + null, sBitmapOptions); + } + if (descriptor != null) { + try { + descriptor.close(); + } catch (IOException e) { + } } + thumbnailDataOut.thumbnail = thumbnail; + thumbnailDataOut.thumbnailInfo = taskThumbnail.thumbnailInfo; } - thumbnailDataOut.thumbnail = thumbnail; - thumbnailDataOut.thumbnailInfo = taskThumbnail.thumbnailInfo; } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java index 58b929ac690a..1a81446c0ad4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java @@ -239,8 +239,7 @@ public class TaskViewThumbnail extends View { // We consider this a stack task if it is not freeform (ie. has no bounds) or has been // dragged into the stack from the freeform workspace boolean isStackTask = !mTask.isFreeformTask() || mTask.bounds == null; - if (mTaskViewRect.isEmpty() || mThumbnailInfo == null || - mThumbnailInfo.taskWidth == 0 || mThumbnailInfo.taskHeight == 0) { + if (mTaskViewRect.isEmpty() || mThumbnailInfo == null) { // If we haven't measured or the thumbnail is invalid, skip the thumbnail drawing // and only draw the background color mThumbnailScale = 0f; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 48a7e7cffa15..5437ad21cbdf 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -16,65 +16,132 @@ package com.android.server.am; -import android.annotation.Nullable; -import android.app.ActivityManagerInternal.PictureInPictureArguments; -import android.app.ApplicationThreadConstants; -import android.app.ContentProviderHolder; -import android.app.IActivityManager; -import android.app.RemoteAction; -import android.app.WaitResult; -import android.os.IDeviceIdentifiersPolicyService; - -import com.android.internal.policy.IKeyguardDismissCallback; -import com.android.internal.telephony.TelephonyIntents; -import com.google.android.collect.Lists; -import com.google.android.collect.Maps; -import com.android.internal.R; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.app.AssistUtils; -import com.android.internal.app.DumpHeapActivity; -import com.android.internal.app.IAppOpsCallback; -import com.android.internal.app.IAppOpsService; -import com.android.internal.app.IVoiceInteractor; -import com.android.internal.app.ProcessMap; -import com.android.internal.app.SystemUserHomeActivity; -import com.android.internal.app.procstats.ProcessStats; -import com.android.internal.os.BackgroundThread; -import com.android.internal.os.BatteryStatsImpl; -import com.android.internal.os.IResultReceiver; -import com.android.internal.os.ProcessCpuTracker; -import com.android.internal.os.TransferPipe; -import com.android.internal.os.Zygote; -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.FastPrintWriter; -import com.android.internal.util.FastXmlSerializer; -import com.android.internal.util.MemInfoReader; -import com.android.internal.util.Preconditions; -import com.android.server.AppOpsService; -import com.android.server.AttributeCache; -import com.android.server.DeviceIdleController; -import com.android.server.IntentResolver; -import com.android.server.LocalServices; -import com.android.server.LockGuard; -import com.android.server.ServiceThread; -import com.android.server.SystemService; -import com.android.server.SystemServiceManager; -import com.android.server.Watchdog; -import com.android.server.am.ActivityStack.ActivityState; -import com.android.server.firewall.IntentFirewall; -import com.android.server.pm.Installer; -import com.android.server.pm.Installer.InstallerException; -import com.android.server.statusbar.StatusBarManagerInternal; -import com.android.server.vr.VrManagerInternal; -import com.android.server.wm.WindowManagerService; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; +import static android.Manifest.permission.CHANGE_CONFIGURATION; +import static android.Manifest.permission.INTERACT_ACROSS_USERS; +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; +import static android.Manifest.permission.READ_FRAME_BUFFER; +import static android.Manifest.permission.START_TASKS_FROM_RECENTS; +import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; +import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW; +import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; +import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; +import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY; +import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; +import static android.content.pm.PackageManager.GET_PROVIDERS; +import static android.content.pm.PackageManager.MATCH_ANY_USER; +import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; +import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; +import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION; +import static android.os.Build.VERSION_CODES.N; +import static android.os.Process.PROC_CHAR; +import static android.os.Process.PROC_OUT_LONG; +import static android.os.Process.PROC_PARENS; +import static android.os.Process.PROC_SPACE_TERM; +import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES; +import static android.provider.Settings.Global.DEBUG_APP; +import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; +import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; +import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL; +import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER; +import static android.provider.Settings.System.FONT_SCALE; +import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.internal.util.XmlUtils.readBooleanAttribute; +import static com.android.internal.util.XmlUtils.readIntAttribute; +import static com.android.internal.util.XmlUtils.readLongAttribute; +import static com.android.internal.util.XmlUtils.writeBooleanAttribute; +import static com.android.internal.util.XmlUtils.writeIntAttribute; +import static com.android.internal.util.XmlUtils.writeLongAttribute; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_BACKGROUND; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CLEANUP; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_URI_PERMISSION; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKSCREEN; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_OOM_ADJ; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_POWER; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESSES; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROVIDER; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_URI_PERMISSION; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.FORCE_NEW_TASK_FLAGS; +import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME; +import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS; +import static com.android.server.am.ActivityStackSupervisor.ON_TOP; +import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; +import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS; +import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS; +import static com.android.server.am.TaskRecord.INVALID_TASK_ID; +import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK; +import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; +import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE; +import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN; +import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_RELAUNCH; +import static com.android.server.wm.AppTransition.TRANSIT_NONE; +import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE; +import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN; +import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT; +import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; +import static org.xmlpull.v1.XmlPullParser.START_TAG; import android.Manifest; import android.Manifest.permission; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; @@ -83,6 +150,7 @@ import android.app.ActivityManager.StackId; import android.app.ActivityManager.StackInfo; import android.app.ActivityManager.TaskThumbnailInfo; import android.app.ActivityManagerInternal; +import android.app.ActivityManagerInternal.PictureInPictureArguments; import android.app.ActivityManagerInternal.SleepToken; import android.app.ActivityOptions; import android.app.ActivityThread; @@ -90,11 +158,14 @@ import android.app.AlertDialog; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.ApplicationErrorReport; +import android.app.ApplicationThreadConstants; import android.app.BroadcastOptions; +import android.app.ContentProviderHolder; import android.app.Dialog; import android.app.IActivityContainer; import android.app.IActivityContainerCallback; import android.app.IActivityController; +import android.app.IActivityManager; import android.app.IAppTask; import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; @@ -111,6 +182,8 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.ProfilerInfo; +import android.app.RemoteAction; +import android.app.WaitResult; import android.app.admin.DevicePolicyManager; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; @@ -155,6 +228,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.Bitmap; +import android.graphics.GraphicBuffer; import android.graphics.Point; import android.graphics.Rect; import android.location.LocationManager; @@ -173,6 +247,7 @@ import android.os.FileObserver; import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; +import android.os.IDeviceIdentifiersPolicyService; import android.os.IPermissionController; import android.os.IProcessInfoService; import android.os.IProgressListener; @@ -200,8 +275,8 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; import android.os.storage.IStorageManager; -import android.os.storage.StorageManagerInternal; import android.os.storage.StorageManager; +import android.os.storage.StorageManagerInternal; import android.provider.Downloads; import android.provider.Settings; import android.service.autofill.AutoFillService; @@ -232,6 +307,54 @@ import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; +import com.google.android.collect.Lists; +import com.google.android.collect.Maps; + +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.app.AssistUtils; +import com.android.internal.app.DumpHeapActivity; +import com.android.internal.app.IAppOpsCallback; +import com.android.internal.app.IAppOpsService; +import com.android.internal.app.IVoiceInteractor; +import com.android.internal.app.ProcessMap; +import com.android.internal.app.SystemUserHomeActivity; +import com.android.internal.app.procstats.ProcessStats; +import com.android.internal.os.BackgroundThread; +import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.os.IResultReceiver; +import com.android.internal.os.ProcessCpuTracker; +import com.android.internal.os.TransferPipe; +import com.android.internal.os.Zygote; +import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FastPrintWriter; +import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.MemInfoReader; +import com.android.internal.util.Preconditions; +import com.android.server.AppOpsService; +import com.android.server.AttributeCache; +import com.android.server.DeviceIdleController; +import com.android.server.IntentResolver; +import com.android.server.LocalServices; +import com.android.server.LockGuard; +import com.android.server.ServiceThread; +import com.android.server.SystemService; +import com.android.server.SystemServiceManager; +import com.android.server.Watchdog; +import com.android.server.am.ActivityStack.ActivityState; +import com.android.server.firewall.IntentFirewall; +import com.android.server.pm.Installer; +import com.android.server.pm.Installer.InstallerException; +import com.android.server.statusbar.StatusBarManagerInternal; +import com.android.server.vr.VrManagerInternal; +import com.android.server.wm.WindowManagerService; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -255,138 +378,15 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.CountDownLatch; import dalvik.system.VMRuntime; - import libcore.io.IoUtils; import libcore.util.EmptyArray; -import static android.Manifest.permission.CHANGE_CONFIGURATION; -import static android.Manifest.permission.INTERACT_ACROSS_USERS; -import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; -import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; -import static android.Manifest.permission.START_TASKS_FROM_RECENTS; -import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; -import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW; -import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.INVALID_STACK_ID; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; -import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; -import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY; -import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; -import static android.content.pm.PackageManager.GET_PROVIDERS; -import static android.content.pm.PackageManager.MATCH_ANY_USER; -import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; -import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; -import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; -import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; -import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION; -import static android.os.Build.VERSION_CODES.N; -import static android.os.Process.PROC_CHAR; -import static android.os.Process.PROC_OUT_LONG; -import static android.os.Process.PROC_PARENS; -import static android.os.Process.PROC_SPACE_TERM; -import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES; -import static android.provider.Settings.Global.DEBUG_APP; -import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; -import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; -import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL; -import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER; -import static android.provider.Settings.System.FONT_SCALE; -import static android.view.Display.DEFAULT_DISPLAY; - -import static com.android.internal.util.XmlUtils.readBooleanAttribute; -import static com.android.internal.util.XmlUtils.readIntAttribute; -import static com.android.internal.util.XmlUtils.readLongAttribute; -import static com.android.internal.util.XmlUtils.writeBooleanAttribute; -import static com.android.internal.util.XmlUtils.writeIntAttribute; -import static com.android.internal.util.XmlUtils.writeLongAttribute; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_BACKGROUND; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CLEANUP; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_URI_PERMISSION; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKSCREEN; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_OOM_ADJ; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_POWER; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESSES; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROVIDER; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_URI_PERMISSION; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND; -import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; -import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.FORCE_NEW_TASK_FLAGS; import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED; -import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME; -import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS; -import static com.android.server.am.ActivityStackSupervisor.ON_TOP; -import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; -import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS; -import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS; -import static com.android.server.am.TaskRecord.INVALID_TASK_ID; -import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK; -import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; -import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE; -import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN; -import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_RELAUNCH; -import static com.android.server.wm.AppTransition.TRANSIT_NONE; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT; -import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; -import static org.xmlpull.v1.XmlPullParser.START_TAG; - public class ActivityManagerService extends IActivityManager.Stub implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { @@ -9654,6 +9654,25 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public GraphicBuffer getTaskSnapshot(int taskId) { + enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()"); + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (this) { + final TaskRecord task = mStackSupervisor.anyTaskForIdLocked( + taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID); + if (task == null) { + Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found"); + return null; + } + return task.getSnapshot(); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override public Bitmap getTaskDescriptionIcon(String filePath, int userId) { if (userId != UserHandle.getCallingUserId()) { enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, @@ -9802,6 +9821,15 @@ public class ActivityManagerService extends IActivityManager.Stub } mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options, "moveTaskToFront", false /* forceNonResizable */); + + final ActivityRecord topActivity = task.getTopActivity(); + if (topActivity != null) { + + // We are reshowing a task, use a starting window to hide the initial draw delay + // so the transition can start earlier. + topActivity.showStartingWindow(null /* prev */, false /* newTask */, + true /* taskSwitch */); + } } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 2963ad1d54f6..47c3e6f80996 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManager.ENABLE_TASK_SNAPSHOTS; import static android.app.ActivityManager.StackId; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; @@ -26,11 +27,11 @@ import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; +import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; import static android.content.pm.ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE; import static android.content.pm.ActivityInfo.FLAG_MULTIPROCESS; import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; -import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; import static android.content.pm.ActivityInfo.FLAG_STATE_NOT_NEEDED; import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP; @@ -103,6 +104,10 @@ import com.android.server.wm.AppWindowContainerController; import com.android.server.wm.AppWindowContainerListener; import com.android.server.wm.TaskWindowContainerController; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + import java.io.File; import java.io.IOException; import java.io.PrintWriter; @@ -113,10 +118,6 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - /** * An entry in the history stack, representing an activity. */ @@ -1204,6 +1205,14 @@ final class ActivityRecord implements AppWindowContainerListener { final Bitmap screenshotActivityLocked() { if (DEBUG_SCREENSHOTS) Slog.d(TAG_SCREENSHOTS, "screenshotActivityLocked: " + this); + + if (ENABLE_TASK_SNAPSHOTS) { + // No need to screenshot if snapshots are enabled. + if (DEBUG_SCREENSHOTS) Slog.d(TAG_SCREENSHOTS, + "\tSnapshots are enabled, abort taking screenshot"); + return null; + } + if (noDisplay) { if (DEBUG_SCREENSHOTS) Slog.d(TAG_SCREENSHOTS, "\tNo display"); return null; @@ -1792,12 +1801,12 @@ final class ActivityRecord implements AppWindowContainerListener { pendingVoiceInteractionStart = false; } - void showStartingWindow(ActivityRecord prev, boolean createIfNeeded) { + void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) { final CompatibilityInfo compatInfo = service.compatibilityInfoForPackageLocked(info.applicationInfo); final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, - prev != null ? prev.appToken : null, createIfNeeded); + prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning()); if (shown) { mStartingWindowState = STARTING_WINDOW_SHOWN; } @@ -2089,6 +2098,14 @@ final class ActivityRecord implements AppWindowContainerListener { preserveWindowOnDeferredRelaunch = false; } + boolean isProcessRunning() { + ProcessRecord proc = app; + if (proc == null) { + proc = service.mProcessNames.get(processName, info.applicationInfo.uid); + } + return proc != null && proc.thread != null; + } + void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { out.attribute(null, ATTR_ID, String.valueOf(createTime)); out.attribute(null, ATTR_LAUNCHEDFROMUID, String.valueOf(launchedFromUid)); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index ab65eb126150..abcaa249744f 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -2459,7 +2459,8 @@ final class ActivityStack extends ConfigurationContainer { next.hasBeenLaunched = true; } else if (SHOW_APP_STARTING_PREVIEW && lastStack != null && mStackSupervisor.isFrontStack(lastStack)) { - next.showStartingWindow(null, true); + next.showStartingWindow(null /* prev */, false /* newTask */, + false /* taskSwitch */); } mStackSupervisor.startSpecificActivityLocked(next, true, false); if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); @@ -2485,7 +2486,8 @@ final class ActivityStack extends ConfigurationContainer { next.hasBeenLaunched = true; } else { if (SHOW_APP_STARTING_PREVIEW) { - next.showStartingWindow(null, true); + next.showStartingWindow(null /* prev */, false /* newTask */, + false /* taskSwich */); } if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next); } @@ -2686,17 +2688,6 @@ final class ActivityStack extends ConfigurationContainer { task.setFrontOfTask(); if (!isHomeOrRecentsStack() || numActivities() > 0) { - // We want to show the starting preview window if we are - // switching to a new task, or the next activity's process is - // not currently running. - boolean showStartingIcon = newTask; - ProcessRecord proc = r.app; - if (proc == null) { - proc = mService.mProcessNames.get(r.processName, r.info.applicationInfo.uid); - } - if (proc == null || proc.thread == null) { - showStartingIcon = true; - } if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: starting " + r); if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { @@ -2756,7 +2747,7 @@ final class ActivityStack extends ConfigurationContainer { prev = null; } } - r.showStartingWindow(prev, showStartingIcon); + r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity)); } } else { // If this is the first activity, don't do any fancy animations, @@ -2765,6 +2756,11 @@ final class ActivityStack extends ConfigurationContainer { } } + private boolean isTaskSwitch(ActivityRecord r, + ActivityRecord topFocusedActivity) { + return topFocusedActivity != null && r.task != topFocusedActivity.task; + } + /** * Perform a reset of the given task, if needed as part of launching it. * Returns the new HistoryRecord at the top of the task. diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 61e3ad5ea052..f401863acf42 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -31,8 +31,8 @@ import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; -import static android.app.ActivityManager.StackId.isStaticStack; import static android.app.ActivityManager.StackId.RECENTS_STACK_ID; +import static android.app.ActivityManager.StackId.isStaticStack; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; @@ -53,7 +53,6 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP; import static android.view.Display.INVALID_DISPLAY; - import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; @@ -106,7 +105,6 @@ import android.hardware.power.V1_0.PowerHint; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; -import android.os.PowerManagerInternal; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -1509,6 +1507,11 @@ class ActivityStarter { mMovedToFront = true; } mOptions = null; + + // We are moving a task to the front, use starting window to hide initial drawn + // delay. + intentActivity.showStartingWindow(null /* prev */, false /* newTask */, + true /* taskSwitch */); } updateTaskReturnToType(intentActivity.task, mLaunchFlags, focusStack); } diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index d0eac775e320..4e44c5f042f1 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -34,6 +34,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.GraphicBuffer; import android.graphics.Point; import android.graphics.Rect; import android.os.Debug; @@ -545,6 +546,13 @@ final class TaskRecord extends ConfigurationContainer { mWindowContainerController.cancelThumbnailTransition(); } + public GraphicBuffer getSnapshot() { + if (mWindowContainerController == null) { + return null; + } + return mWindowContainerController.getSnapshot(); + } + void touchActiveTime() { lastActiveTime = System.currentTimeMillis(); if (firstActiveTime == 0) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 0a312f0ad7ee..4b2b184d1bbc 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -51,6 +51,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_ST import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT; @@ -2907,7 +2908,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Only return the view if it was successfully added to the // window manager... which we can tell by it having a parent. - return view.getParent() != null ? new SplashScreenSurface(view) : null; + return view.getParent() != null ? new SplashScreenSurface(view, appToken) : null; } catch (WindowManager.BadTokenException e) { // ignore Log.w(TAG, appToken + " already running, starting window not displayed. " + @@ -2927,18 +2928,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { return null; } - /** {@inheritDoc} */ - @Override - public void removeSplashScreen(IBinder appToken, StartingSurface surface) { - if (DEBUG_SPLASH_SCREEN) Slog.v(TAG, "Removing splash screen window for " + appToken + ": " - + surface + " Callers=" + Debug.getCallers(4)); - - if (surface != null) { - WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); - wm.removeView(((SplashScreenSurface) surface).view); - } - } - /** * Preflight adding a window to the system. * @@ -5185,7 +5174,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs, WindowState attached, WindowState imeTarget) { - final boolean visible = win.isVisibleLw(); + final boolean visible = !win.isGoneForLayoutLw(); if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisible=" + visible); applyKeyguardPolicyLw(win, imeTarget); final int fl = PolicyControl.getWindowFlags(win, attrs); @@ -5202,8 +5191,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + // Don't allow snapshots to influence SystemUI visibility flags. + // TODO: Revisit this once SystemUI flags for snapshots are handled correctly boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW - && attrs.type < FIRST_SYSTEM_WINDOW; + && attrs.type < FIRST_SYSTEM_WINDOW + && (attrs.privateFlags & PRIVATE_FLAG_TASK_SNAPSHOT) == 0; final int stackId = win.getStackId(); if (mTopFullscreenOpaqueWindowState == null && visible) { if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) { diff --git a/services/core/java/com/android/server/policy/SplashScreenSurface.java b/services/core/java/com/android/server/policy/SplashScreenSurface.java index d4212915c0a6..37d6c0b55d54 100644 --- a/services/core/java/com/android/server/policy/SplashScreenSurface.java +++ b/services/core/java/com/android/server/policy/SplashScreenSurface.java @@ -16,7 +16,13 @@ package com.android.server.policy; +import static com.android.server.policy.PhoneWindowManager.DEBUG_SPLASH_SCREEN; + +import android.os.Debug; +import android.os.IBinder; +import android.util.Slog; import android.view.View; +import android.view.WindowManager; import android.view.WindowManagerPolicy; import android.view.WindowManagerPolicy.StartingSurface; @@ -30,9 +36,21 @@ import com.android.internal.policy.PhoneWindow; */ class SplashScreenSurface implements StartingSurface { - final View view; + private static final String TAG = PhoneWindowManager.TAG; + private final View mView; + private final IBinder mAppToken; + + SplashScreenSurface(View view, IBinder appToken) { + mView = view; + mAppToken = appToken; + } + + @Override + public void remove() { + if (DEBUG_SPLASH_SCREEN) Slog.v(TAG, "Removing splash screen window for " + mAppToken + ": " + + this + " Callers=" + Debug.getCallers(4)); - SplashScreenSurface(View view) { - this.view = view; + final WindowManager wm = mView.getContext().getSystemService(WindowManager.class); + wm.removeView(mView); } } diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index d2f604d67132..4dbe35f2592b 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -31,6 +31,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.GraphicBuffer; import android.os.Debug; import android.os.Handler; import android.os.IBinder; @@ -50,6 +51,10 @@ import com.android.server.AttributeCache; public class AppWindowContainerController extends WindowContainerController { + private static final int STARTING_WINDOW_TYPE_NONE = 0; + private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1; + private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2; + private final IApplicationToken mToken; private final Handler mHandler = new Handler(Looper.getMainLooper()); @@ -80,16 +85,39 @@ public class AppWindowContainerController mListener.onWindowsGone(); }; + private final Runnable mRemoveStartingWindow = () -> { + StartingSurface surface = null; + StartingData data = null; + synchronized (mWindowMap) { + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting " + mContainer + + ": startingWindow=" + mContainer.startingWindow + + " startingView=" + mContainer.startingSurface); + if (mContainer.startingWindow != null) { + surface = mContainer.startingSurface; + data = mContainer.startingData; + mContainer.startingData = null; + mContainer.startingSurface = null; + mContainer.startingWindow = null; + mContainer.startingDisplayed = false; + } + } + if (data != null && surface != null) { + try { + surface.remove(); + } catch (Exception e) { + Slog.w(TAG_WM, "Exception when removing starting window", e); + } + } + }; + private final Runnable mAddStartingWindow = () -> { final StartingData startingData; - final Configuration mergedOverrideConfiguration; synchronized (mWindowMap) { if (mContainer == null) { return; } startingData = mContainer.startingData; - mergedOverrideConfiguration = mContainer.getMergedOverrideConfiguration(); } if (startingData == null) { @@ -98,20 +126,16 @@ public class AppWindowContainerController } if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting " - + this + ": pkg=" + mContainer.startingData.pkg); + + this + ": startingData=" + mContainer.startingData); - StartingSurface contents = null; + StartingSurface surface = null; try { - contents = mService.mPolicy.addSplashScreen(mContainer.token, startingData.pkg, - startingData.theme, startingData.compatInfo, startingData.nonLocalizedLabel, - startingData.labelRes, startingData.icon, startingData.logo, - startingData.windowFlags, mergedOverrideConfiguration); + surface = startingData.createStartingSurface(); } catch (Exception e) { Slog.w(TAG_WM, "Exception when adding starting window", e); } - if (contents != null) { + if (surface != null) { boolean abort = false; - synchronized(mWindowMap) { if (mContainer.removed || mContainer.startingData == null) { // If the window was successfully added, then @@ -121,12 +145,10 @@ public class AppWindowContainerController "Aborted starting " + mContainer + ": removed=" + mContainer.removed + " startingData=" + mContainer.startingData); - mContainer.startingWindow = null; - mContainer.startingData = null; abort = true; } } else { - mContainer.startingSurface = contents; + mContainer.startingSurface = surface; } if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM, "Added starting " + mContainer @@ -134,42 +156,11 @@ public class AppWindowContainerController + mContainer.startingWindow + " startingView=" + mContainer.startingSurface); } - if (abort) { - try { - mService.mPolicy.removeSplashScreen(mContainer.token, contents); - } catch (Exception e) { - Slog.w(TAG_WM, "Exception when removing starting window", e); - } - } - } - }; - - private final Runnable mRemoveStartingWindow = () -> { - IBinder token = null; - StartingSurface contents = null; - synchronized (mWindowMap) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting " - + mContainer + ": startingWindow=" - + mContainer.startingWindow + " startingView=" - + mContainer.startingSurface); + mRemoveStartingWindow.run(); if (mContainer == null) { return; } - if (mContainer.startingWindow != null) { - contents = mContainer.startingSurface; - token = mContainer.token; - mContainer.startingData = null; - mContainer.startingSurface = null; - mContainer.startingWindow = null; - mContainer.startingDisplayed = false; - } - } - if (contents != null) { - try { - mService.mPolicy.removeSplashScreen(token, contents); - } catch (Exception e) { - Slog.w(TAG_WM, "Exception when removing starting window", e); } } }; @@ -389,7 +380,7 @@ public class AppWindowContainerController public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, - IBinder transferFrom, boolean createIfNeeded) { + IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning) { synchronized(mWindowMap) { if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken + " pkg=" + pkg + " transferFrom=" + transferFrom); @@ -409,6 +400,12 @@ public class AppWindowContainerController return false; } + final int type = getStartingWindowType(newTask, taskSwitch, processRunning); + + if (type == STARTING_WINDOW_TYPE_SNAPSHOT) { + return createSnapshot(); + } + // If this is a translucent window, then don't show a starting window -- the current // effect (a full-screen opaque starting window that fades away to the real contents // when it is ready) does not work for this. @@ -458,22 +455,32 @@ public class AppWindowContainerController return true; } - // There is no existing starting window, and the caller doesn't - // want us to create one, so that's it! - if (!createIfNeeded) { + // There is no existing starting window, and we don't want to create a splash screen, so + // that's it! + if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) { return false; } if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating StartingData"); - mContainer.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel, - labelRes, icon, logo, windowFlags); + mContainer.startingData = new SplashScreenStartingData(mService, mContainer, pkg, theme, + compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, + mContainer.getMergedOverrideConfiguration()); scheduleAddStartingWindow(); } return true; } - void scheduleAddStartingWindow() { + private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning) { + if (newTask || !processRunning) { + return STARTING_WINDOW_TYPE_SPLASH_SCREEN; + } else if (taskSwitch) { + return STARTING_WINDOW_TYPE_SNAPSHOT; + } else { + return STARTING_WINDOW_TYPE_NONE; + } + } + void scheduleAddStartingWindow() { // Note: we really want to do sendMessageAtFrontOfQueue() because we // want to process the message ASAP, before any other queued // messages. @@ -481,6 +488,19 @@ public class AppWindowContainerController mHandler.postAtFrontOfQueue(mAddStartingWindow); } + private boolean createSnapshot() { + final GraphicBuffer snapshot = mService.mTaskSnapshotController.getSnapshot( + mContainer.mTask); + + if (snapshot == null) { + return false; + } + + mContainer.startingData = new SnapshotStartingData(mService, mContainer, snapshot); + scheduleAddStartingWindow(); + return true; + } + public void removeStartingWindow() { synchronized (mWindowMap) { if (mHandler.hasCallbacks(mRemoveStartingWindow)) { @@ -593,7 +613,7 @@ public class AppWindowContainerController } return dc.screenshotApplications(mToken.asBinder(), width, height, false /* includeFullDisplay */, frameScale, Bitmap.Config.RGB_565, - false /* wallpaperOnly */); + false /* wallpaperOnly */, false /* includeDecor */, true /* toAshmem */); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index f4fa2206a22a..10d1d8b7c1c4 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -205,7 +205,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // We now have a good window to show, remove dead placeholders removeDeadWindows(); - if (startingData != null) { + if (startingWindow != null) { if (DEBUG_STARTING_WINDOW || DEBUG_ANIM) Slog.v(TAG, "Finish starting " + win.mToken + ": first real window is shown, no animation"); // If this initial window is animating, stop it -- we will do an animation to reveal @@ -671,7 +671,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } else if (mChildren.size() == 0 && startingData != null) { // If this is the last window and we had requested a starting transition window, // well there is no point now. - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingWindow"); + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingData"); startingData = null; } else if (mChildren.size() == 1 && startingSurface != null) { // If this is the last window except for a starting transition window, diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index d86c4dab54fb..66267bdb018c 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -106,6 +106,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; import android.util.DisplayMetrics; +import android.util.MutableBoolean; import android.util.Slog; import android.view.Display; import android.view.DisplayInfo; @@ -2102,10 +2103,14 @@ class DisplayContent extends WindowContainer + * Access to this class should be guarded by the global window manager lock. + */ +class TaskSnapshotCache { + + private final ArrayMap mCache = new ArrayMap<>(); + + void putSnapshot(Task task, GraphicBuffer snapshot) { + mCache.put(task, snapshot); + } + + @Nullable GraphicBuffer getSnapshot(Task task) { + return mCache.get(task); + } +} diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java new file mode 100644 index 000000000000..d08c744c38ff --- /dev/null +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.wm; + +import static android.app.ActivityManager.ENABLE_TASK_SNAPSHOTS; +import static android.graphics.Bitmap.Config.ARGB_8888; +import static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE; +import static android.graphics.GraphicBuffer.USAGE_SW_READ_NEVER; +import static android.graphics.GraphicBuffer.USAGE_SW_WRITE_NEVER; +import static android.graphics.PixelFormat.RGBA_8888; + +import android.annotation.Nullable; +import android.app.ActivityManager.StackId; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.GraphicBuffer; +import android.util.ArraySet; +import android.view.WindowManagerPolicy.StartingSurface; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * When an app token becomes invisible, we take a snapshot (bitmap) of the corresponding task and + * put it into our cache. Internally we use gralloc buffers to be able to draw them wherever we + * like without any copying. + *

+ * System applications may retrieve a snapshot to represent the current state of a task, and draw + * them in their own process. + *

+ * When we task becomes visible again, we show a starting window with the snapshot as the content to + * make app transitions more responsive. + *

+ * To access this class, acquire the global window manager lock. + */ +class TaskSnapshotController { + + private final WindowManagerService mService; + private final TaskSnapshotCache mCache = new TaskSnapshotCache(); + + private final ArraySet mTmpTasks = new ArraySet<>(); + + TaskSnapshotController(WindowManagerService service) { + mService = service; + } + + void onTransitionStarting() { + if (!ENABLE_TASK_SNAPSHOTS) { + return; + } + + // We need to take a snapshot of the task if and only if all activities of the task are + // either closing or hidden. + getClosingTasks(mService.mClosingApps, mTmpTasks); + for (int i = mTmpTasks.size() - 1; i >= 0; i--) { + final Task task = mTmpTasks.valueAt(i); + if (!canSnapshotTask(task)) { + continue; + } + final GraphicBuffer graphicBuffer = snapshotTask(task); + if (graphicBuffer != null) { + mCache.putSnapshot(task, graphicBuffer); + } + } + } + + @Nullable GraphicBuffer getSnapshot(Task task) { + return mCache.getSnapshot(task); + } + + /** + * Creates a starting surface for {@param token} with {@param snapshot}. DO NOT HOLD THE WINDOW + * MANAGER LOCK WHEN CALLING THIS METHOD! + */ + StartingSurface createStartingSurface(AppWindowToken token, + GraphicBuffer snapshot) { + return TaskSnapshotSurface.create(mService, token, snapshot); + } + + private GraphicBuffer snapshotTask(Task task) { + final AppWindowToken top = (AppWindowToken) task.getTop(); + if (top == null) { + return null; + } + final Bitmap bmp = top.mDisplayContent.screenshotApplications(top.token, -1, -1, false, + 1.0f, ARGB_8888, false, true, false); + if (bmp == null) { + return null; + } + // TODO: Already use a GraphicBuffer when snapshotting the content. + final GraphicBuffer buffer = GraphicBuffer.create(bmp.getWidth(), bmp.getHeight(), + RGBA_8888, USAGE_HW_TEXTURE | USAGE_SW_WRITE_NEVER | USAGE_SW_READ_NEVER); + final Canvas c = buffer.lockCanvas(); + c.drawBitmap(bmp, 0, 0, null); + buffer.unlockCanvasAndPost(c); + return buffer; + } + + /** + * Retrieves all closing tasks based on the list of closing apps during an app transition. + */ + @VisibleForTesting + void getClosingTasks(ArraySet closingApps, ArraySet outClosingTasks) { + outClosingTasks.clear(); + for (int i = closingApps.size() - 1; i >= 0; i--) { + final AppWindowToken atoken = closingApps.valueAt(i); + + // If the task of the app is not visible anymore, it means no other app in that task + // is opening. Thus, the task is closing. + if (atoken.mTask != null && !atoken.mTask.isVisible()) { + outClosingTasks.add(closingApps.valueAt(i).mTask); + } + } + } + + private boolean canSnapshotTask(Task task) { + return !StackId.isHomeOrRecentsStack(task.mStack.mStackId); + } +} diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java new file mode 100644 index 000000000000..c3e314149c66 --- /dev/null +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.GraphicBuffer; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Message; +import android.os.RemoteException; +import android.util.Slog; +import android.view.Display; +import android.view.IWindowSession; +import android.view.Surface; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; +import android.view.WindowManagerPolicy.StartingSurface; + +import com.android.internal.view.BaseIWindow; + +/** + * This class represents a starting window that shows a snapshot. + *

+ * DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING METHODS OF THIS CLASS! + */ +class TaskSnapshotSurface implements StartingSurface { + + private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM; + private static final int MSG_REPORT_DRAW = 0; + private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s"; + private final Window mWindow; + private final Surface mSurface; + private final IWindowSession mSession; + private final WindowManagerService mService; + private boolean mHasDrawn; + private boolean mReportNextDraw; + + static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token, + GraphicBuffer snapshot) { + + final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); + final Window window = new Window(); + final IWindowSession session = WindowManagerGlobal.getWindowSession(); + window.setSession(session); + final Surface surface = new Surface(); + final Rect tmpRect = new Rect(); + final Rect tmpFrame = new Rect(); + final Configuration tmpConfiguration = new Configuration(); + synchronized (service.mWindowMap) { + layoutParams.type = TYPE_APPLICATION_STARTING; + layoutParams.format = snapshot.getFormat(); + layoutParams.flags = FLAG_LAYOUT_INSET_DECOR + | FLAG_LAYOUT_IN_SCREEN + | FLAG_NOT_FOCUSABLE + | FLAG_NOT_TOUCHABLE + | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + layoutParams.privateFlags = PRIVATE_FLAG_TASK_SNAPSHOT; + layoutParams.token = token.token; + layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.height = LayoutParams.MATCH_PARENT; + + // TODO: Inherit behavior whether to draw behind status bar/nav bar. + layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + layoutParams.setTitle(String.format(TITLE_FORMAT, token.mTask.mTaskId)); + } + try { + final int res = session.addToDisplay(window, window.mSeq, layoutParams, + View.VISIBLE, token.getDisplayContent().getDisplayId(), tmpRect, tmpRect, + tmpRect, null); + if (res < 0) { + Slog.w(TAG, "Failed to add snapshot starting window res=" + res); + return null; + } + } catch (RemoteException e) { + // Local call. + } + final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window, + surface); + window.setOuter(snapshotSurface); + try { + session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame, + tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpConfiguration, + surface); + } catch (RemoteException e) { + // Local call. + } + snapshotSurface.drawSnapshot(snapshot); + return snapshotSurface; + } + + private TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface) { + mService = service; + mSession = WindowManagerGlobal.getWindowSession(); + mWindow = window; + mSurface = surface; + } + + @Override + public void remove() { + try { + mSession.remove(mWindow); + } catch (RemoteException e) { + // Local call. + } + } + + private void drawSnapshot(GraphicBuffer snapshot) { + + // TODO: Just wrap the buffer here without any copying. + final Canvas c = mSurface.lockHardwareCanvas(); + c.drawBitmap(Bitmap.createHardwareBitmap(snapshot), 0, 0, null); + mSurface.unlockCanvasAndPost(c); + final boolean reportNextDraw; + synchronized (mService.mWindowMap) { + mHasDrawn = true; + reportNextDraw = mReportNextDraw; + } + if (reportNextDraw) { + reportDrawn(); + } + } + + private void reportDrawn() { + synchronized (mService.mWindowMap) { + mReportNextDraw = false; + } + try { + mSession.finishDrawing(mWindow); + } catch (RemoteException e) { + // Local call. + } + } + + private static Handler sHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_REPORT_DRAW: + final boolean hasDrawn; + final TaskSnapshotSurface surface = (TaskSnapshotSurface) msg.obj; + synchronized (surface.mService.mWindowMap) { + hasDrawn = surface.mHasDrawn; + if (!hasDrawn) { + surface.mReportNextDraw = true; + } + } + if (hasDrawn) { + surface.reportDrawn(); + } + break; + } + } + }; + + private static class Window extends BaseIWindow { + + private TaskSnapshotSurface mOuter; + + public void setOuter(TaskSnapshotSurface outer) { + mOuter = outer; + } + + @Override + public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, + Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig, + Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar) { + if (reportDraw) { + sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget(); + } + } + } +} diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java index 9c303f811590..1d598e7b4478 100644 --- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java +++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java @@ -17,6 +17,7 @@ package com.android.server.wm; import android.content.res.Configuration; +import android.graphics.GraphicBuffer; import android.graphics.Rect; import android.util.EventLog; import android.util.Slog; @@ -238,6 +239,19 @@ public class TaskWindowContainerController } } + /** + * @return a graphic buffer representing a screenshot of a task + */ + public GraphicBuffer getSnapshot() { + synchronized (mWindowMap) { + if (mContainer == null) { + Slog.w(TAG_WM, "getSnapshot: taskId " + mTaskId + " not found."); + return null; + } + return mService.mTaskSnapshotController.getSnapshot(mContainer); + } + } + @Override public String toString() { return "{TaskWindowContainerController taskId=" + mTaskId + "}"; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 048affb74728..3639fb0d26e3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -39,6 +39,7 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.database.ContentObserver; import android.graphics.Bitmap; +import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; import android.graphics.Matrix; import android.graphics.Point; @@ -183,6 +184,7 @@ import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHA import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; @@ -580,6 +582,7 @@ public class WindowManagerService extends IWindowManager.Stub final UnknownAppVisibilityController mUnknownAppVisibilityController = new UnknownAppVisibilityController(this); + final TaskSnapshotController mTaskSnapshotController = new TaskSnapshotController(this); boolean mIsTouchDevice; @@ -1215,7 +1218,9 @@ public class WindowManagerService extends IWindowManager.Stub + token + ". Aborting."); return WindowManagerGlobal.ADD_APP_EXITING; } - if (rootType == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) { + if (rootType == TYPE_APPLICATION_STARTING + && (attrs.privateFlags & PRIVATE_FLAG_TASK_SNAPSHOT) == 0 + && atoken.firstWindowDrawn) { // No need for this guy! if (DEBUG_STARTING_WINDOW || localLOGV) Slog.v( TAG_WM, "**** NO NEED TO START: " + attrs.getTitle()); @@ -1966,7 +1971,8 @@ public class WindowManagerService extends IWindowManager.Stub + " newVis=" + viewVisibility, stack); } if (viewVisibility == View.VISIBLE && - (win.mAppToken == null || !win.mAppToken.clientHidden)) { + (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING + || !win.mAppToken.clientHidden)) { result = relayoutVisibleWindow(outConfig, result, win, winAnimator, attrChanges, oldVisibility); try { @@ -2073,7 +2079,10 @@ public class WindowManagerService extends IWindowManager.Stub win.setDisplayLayoutNeeded(); win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; configChanged = updateOrientationFromAppTokensLocked(false, displayId); - mWindowPlacerLocked.performSurfacePlacement(); + + // We may be deferring layout passes at the moment, but since the client is interested + // in the new out values right now we need to force a layout. + mWindowPlacerLocked.performSurfacePlacement(true /* force */); if (toBeDisplayed && win.mIsWallpaper) { DisplayInfo displayInfo = win.getDisplayContent().getDisplayInfo(); dc.mWallpaperController.updateWallpaperOffset( @@ -3858,7 +3867,8 @@ public class WindowManagerService extends IWindowManager.Stub Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper"); return screenshotApplications(null /* appToken */, DEFAULT_DISPLAY, -1 /* width */, -1 /* height */, true /* includeFullDisplay */, 1f /* frameScale */, - Bitmap.Config.ARGB_8888, true /* wallpaperOnly */); + Bitmap.Config.ARGB_8888, true /* wallpaperOnly */, false /* includeDecor */, + true /* toAshmem */); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } @@ -3879,7 +3889,8 @@ public class WindowManagerService extends IWindowManager.Stub FgThread.getHandler().post(() -> { Bitmap bm = screenshotApplications(null /* appToken */, DEFAULT_DISPLAY, -1 /* width */, -1 /* height */, true /* includeFullDisplay */, - 1f /* frameScale */, Bitmap.Config.ARGB_8888, false /* wallpaperOnly */); + 1f /* frameScale */, Bitmap.Config.ARGB_8888, false /* wallpaperOnly */, + false /* includeDecor */, true /* toAshmem */); try { receiver.send(bm); } catch (RemoteException e) { @@ -3900,10 +3911,14 @@ public class WindowManagerService extends IWindowManager.Stub * @param frameScale the scale to apply to the frame, only used when width = -1 and height = -1 * @param config of the output bitmap * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot + * @param includeDecor whether to include window decors, like the status or navigation bar + * background of the window + * @param toAshmem whether to convert the resulting bitmap to ashmem; this should be set to + * true if the Bitmap is sent over binder, and false otherwise */ private Bitmap screenshotApplications(IBinder appToken, int displayId, int width, int height, boolean includeFullDisplay, float frameScale, Bitmap.Config config, - boolean wallpaperOnly) { + boolean wallpaperOnly, boolean includeDecor, boolean toAshmem) { final DisplayContent displayContent; synchronized(mWindowMap) { displayContent = mRoot.getDisplayContentOrCreate(displayId); @@ -3914,7 +3929,7 @@ public class WindowManagerService extends IWindowManager.Stub } } return displayContent.screenshotApplications(appToken, width, height, - includeFullDisplay, frameScale, config, wallpaperOnly); + includeFullDisplay, frameScale, config, wallpaperOnly, includeDecor, toAshmem); } /** diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 3daad4373d06..b0945ee34243 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -16,55 +16,11 @@ package com.android.server.wm; -import android.app.ActivityManager; -import android.app.AppOpsManager; -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.Matrix; -import android.graphics.PixelFormat; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.Region; -import android.os.Binder; -import android.os.Debug; -import android.os.IBinder; -import android.os.PowerManager; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.Trace; -import android.os.UserHandle; -import android.os.WorkSource; -import android.util.DisplayMetrics; -import android.util.Slog; -import android.util.TimeUtils; -import android.view.DisplayInfo; -import android.view.Gravity; -import android.view.IApplicationToken; -import android.view.IWindow; -import android.view.IWindowFocusObserver; -import android.view.IWindowId; -import android.view.InputChannel; -import android.view.InputEvent; -import android.view.InputEventReceiver; -import android.view.View; -import android.view.ViewTreeObserver; -import android.view.WindowInfo; -import android.view.WindowManager; -import android.view.WindowManagerPolicy; - -import com.android.internal.util.ToBooleanFunction; -import com.android.server.input.InputWindowHandle; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.LinkedList; -import java.util.function.Predicate; - +import static android.app.ActivityManager.ENABLE_TASK_SNAPSHOTS; import static android.app.ActivityManager.StackId; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.ActivityManager.isLowRamDeviceStatic; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; @@ -96,8 +52,8 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; @@ -138,6 +94,51 @@ import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW; +import android.app.AppOpsManager; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Matrix; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.Region; +import android.os.Binder; +import android.os.Debug; +import android.os.IBinder; +import android.os.PowerManager; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.Trace; +import android.os.UserHandle; +import android.os.WorkSource; +import android.util.DisplayMetrics; +import android.util.Slog; +import android.util.TimeUtils; +import android.view.DisplayInfo; +import android.view.Gravity; +import android.view.IApplicationToken; +import android.view.IWindow; +import android.view.IWindowFocusObserver; +import android.view.IWindowId; +import android.view.InputChannel; +import android.view.InputEvent; +import android.view.InputEventReceiver; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.WindowInfo; +import android.view.WindowManager; +import android.view.WindowManagerPolicy; + +import com.android.internal.util.ToBooleanFunction; +import com.android.server.input.InputWindowHandle; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.function.Predicate; + /** A window in the window manager. */ class WindowState extends WindowContainer implements WindowManagerPolicy.WindowState { static final String TAG = TAG_WITH_CLASS_NAME ? "WindowState" : TAG_WM; @@ -152,7 +153,8 @@ class WindowState extends WindowContainer implements WindowManagerP // to capture touch events in that area. static final int RESIZE_HANDLE_WIDTH_IN_DP = 30; - private static final boolean DEBUG_DISABLE_SAVING_SURFACES = false; + private static final boolean DEBUG_DISABLE_SAVING_SURFACES = false || + ENABLE_TASK_SNAPSHOTS; final WindowManagerService mService; final WindowManagerPolicy mPolicy; @@ -2652,7 +2654,7 @@ class WindowState extends WindowContainer implements WindowManagerP return false; } - if (ActivityManager.isLowRamDeviceStatic()) { + if (isLowRamDeviceStatic()) { // Don't save surfaces on Svelte devices. return false; } diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 4df100105bcb..897d5b86d3d9 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -117,7 +117,11 @@ class WindowSurfacePlacer { } final void performSurfacePlacement() { - if (mDeferDepth > 0) { + performSurfacePlacement(false /* force */); + } + + final void performSurfacePlacement(boolean force) { + if (mDeferDepth > 0 && !force) { return; } int loopCount = 6; @@ -343,6 +347,8 @@ class WindowSurfacePlacer { mService.mAppTransition.postAnimationCallback(); mService.mAppTransition.clear(); + mService.mTaskSnapshotController.onTransitionStarting(); + mService.mOpeningApps.clear(); mService.mClosingApps.clear(); mService.mUnknownAppVisibilityController.clear(); @@ -513,17 +519,14 @@ class WindowSurfacePlacer { + wtoken.startingMoved + " isRelaunching()=" + wtoken.isRelaunching()); - if (wtoken.isRelaunching()) { - return false; - } - final boolean drawnBeforeRestoring = wtoken.allDrawn; wtoken.restoreSavedSurfaceForInterestingWindows(); - if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) { + final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching(); + if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) { return false; } - if (wtoken.allDrawn) { + if (allDrawn) { reason = drawnBeforeRestoring ? APP_TRANSITION_WINDOWS_DRAWN : APP_TRANSITION_SAVED_SURFACE; } else { diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java new file mode 100644 index 000000000000..5dff9973f734 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; +import static com.android.server.wm.AppTransition.TRANSIT_UNSET; +import static junit.framework.Assert.assertEquals; + +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.util.ArraySet; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test class for {@link TaskSnapshotController}. + * + * runtest frameworks-services -c com.android.server.wm.TaskSnapshotControllerTest + */ +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class TaskSnapshotControllerTest extends WindowTestsBase { + + @Test + public void testGetClosingApps_closing() throws Exception { + final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, + "closingWindow"); + closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET, + true /* performLayout */, false /* isVoiceInteraction */); + final ArraySet closingApps = new ArraySet<>(); + closingApps.add(closingWindow.mAppToken); + final ArraySet closingTasks = new ArraySet<>(); + sWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks); + assertEquals(1, closingTasks.size()); + assertEquals(closingWindow.mAppToken.mTask, closingTasks.valueAt(0)); + } + + @Test + public void testGetClosingApps_notClosing() throws Exception { + final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, + "closingWindow"); + final WindowState openingWindow = createAppWindow(closingWindow.getTask(), + FIRST_APPLICATION_WINDOW, "openingWindow"); + closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET, + true /* performLayout */, false /* isVoiceInteraction */); + openingWindow.mAppToken.setVisibility(null, true /* visible */, TRANSIT_UNSET, + true /* performLayout */, false /* isVoiceInteraction */); + final ArraySet closingApps = new ArraySet<>(); + closingApps.add(closingWindow.mAppToken); + final ArraySet closingTasks = new ArraySet<>(); + sWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks); + assertEquals(0, closingTasks.size()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index c4fd7221d3f4..12e7a15d2cbb 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -313,11 +313,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public void removeSplashScreen(IBinder appToken, StartingSurface surface) { - - } - - @Override public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) { return 0; diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index be080f56fb10..466bd6700f16 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -119,6 +119,12 @@ class WindowTestsBase { : createWindow(parent, type, parent.mToken, name); } + WindowState createAppWindow(Task task, int type, String name) { + final AppWindowToken token = new TestAppWindowToken(sDisplayContent); + task.addChild(token, 0); + return createWindow(null, type, token, name); + } + WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) { final WindowToken token = createWindowToken(dc, type); return createWindow(parent, type, token, name); diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 5e5ebd7e6488..33f147670f90 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -16,6 +16,7 @@ package android.view; +import android.graphics.GraphicBuffer; import android.graphics.Point; import android.graphics.Rect; import com.android.internal.app.IAssistScreenshotReceiver; -- 2.11.0