Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 02886a82 authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

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
parent e86e6d75
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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;

+6 −0
Original line number Diff line number Diff line
@@ -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
+11 −0
Original line number Diff line number Diff line
@@ -1318,6 +1318,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.
         *
         * <p>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
+8 −24
Original line number Diff line number Diff line
@@ -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,34 +751,13 @@ 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.
     *
     * <p>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
+40 −19
Original line number Diff line number Diff line
@@ -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,6 +630,23 @@ public class SystemServicesProxy {
            return;
        }

        if (ActivityManager.ENABLE_TASK_SNAPSHOTS) {
            GraphicBuffer graphicBuffer = null;
            try {
                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;
@@ -647,6 +667,7 @@ public class SystemServicesProxy {
            thumbnailDataOut.thumbnail = thumbnail;
            thumbnailDataOut.thumbnailInfo = taskThumbnail.thumbnailInfo;
        }
    }

    /**
     * Moves a task into another stack.
Loading