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

Commit e74d7bf2 authored by wilsonshih's avatar wilsonshih
Browse files

Introduce ActivitySnapshotController(3/N)

The AppSnapshotController is response for transition analysis, so
TaskSnapshotController and ActivitySnapshotController does not need
to do duplicate analysis. Also, it should be easier to do test by
de-couple the "transition-analysiser" and "transition-handler".

And for the basic implementation of Activity snapshot controller:
1. Record and/or remove an activity snapshot when an activity
transition happen.
2. Delete and/or load an activity snapshot from disk to cache when a
task transition happen.
3. Only keep one snapshot for the visible task in the cache.
4. This feature is disabled on Low Ram device.
5. Persisted snapshot file will be removed when activity removed.
6. All persisted files for a user will be removed while first access.
7. Purge persisted files on disk when persited file exceed a maximum
count.
8. Works on multi-user environment.

Bug: 259497289
Test: atest TransitionTests ActivitySnapshotControllerTests\
AppSnapshotControllerTests TaskSnapshotControllerTest

Change-Id: I4fc0ca96c748c6123ffabcab9c6b03b7057ac840
parent 99e93940
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -3254,6 +3254,9 @@
    <!-- Feature flag to store TaskSnapshot in 16 bit pixel format to save memory. -->
    <bool name="config_use16BitTaskSnapshotPixelFormat">false</bool>

    <!-- The amount to scale fullscreen activity snapshot for predict-back animation. -->
    <item name="config_resActivitySnapshotScale" format="float" type="dimen">0.6</item>

    <!-- Determines whether recent tasks are provided to the user. Default device has recents
         property. If this is false, then the following recents config flags are ignored. -->
    <bool name="config_hasRecents">true</bool>
+1 −0
Original line number Diff line number Diff line
@@ -362,6 +362,7 @@
  <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
  <java-symbol type="dimen" name="config_highResTaskSnapshotScale" />
  <java-symbol type="dimen" name="config_lowResTaskSnapshotScale" />
  <java-symbol type="dimen" name="config_resActivitySnapshotScale" />
  <java-symbol type="dimen" name="config_qsTileStrokeWidthInactive" />
  <java-symbol type="dimen" name="config_qsTileStrokeWidthActive" />
  <java-symbol type="bool" name="config_use16BitTaskSnapshotPixelFormat" />
+32 −12
Original line number Diff line number Diff line
@@ -55,8 +55,8 @@ import java.io.PrintWriter;
 * @param <CACHE> The basic cache for either Task or ActivityRecord
 */
abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
        CACHE extends AbsAppSnapshotCache<TYPE>> {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotController" : TAG_WM;
        CACHE extends SnapshotCache<TYPE>> {
    static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotController" : TAG_WM;
    /**
     * Return value for {@link #getSnapshotMode}: We are allowed to take a real screenshot to be
     * used as the snapshot.
@@ -76,7 +76,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
    static final int SNAPSHOT_MODE_NONE = 2;

    protected final WindowManagerService mService;
    protected final float mHighResTaskSnapshotScale;
    protected final float mHighResSnapshotScale;
    private final Rect mTmpRect = new Rect();
    /**
     * Flag indicating whether we are running on an Android TV device.
@@ -99,12 +99,13 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
                PackageManager.FEATURE_LEANBACK);
        mIsRunningOnIoT = mService.mContext.getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_EMBEDDED);
        mHighResTaskSnapshotScale = initSnapshotScale();
        mHighResSnapshotScale = initSnapshotScale();
    }

    protected float initSnapshotScale() {
        return mService.mContext.getResources().getFloat(
        final float config = mService.mContext.getResources().getFloat(
                com.android.internal.R.dimen.config_highResTaskSnapshotScale);
        return Math.max(Math.min(config, 1f), 0.1f);
    }

    /**
@@ -173,7 +174,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
        final HardwareBuffer buffer = snapshot.getHardwareBuffer();
        if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
            buffer.close();
            Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
            Slog.e(TAG, "Invalid snapshot dimensions " + buffer.getWidth() + "x"
                    + buffer.getHeight());
            return null;
        } else {
@@ -223,7 +224,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
        Point taskSize = new Point();
        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "createSnapshot");
        final ScreenCapture.ScreenshotHardwareBuffer taskSnapshot = createSnapshot(source,
                mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize, builder);
                mHighResSnapshotScale, builder.getPixelFormat(), taskSize, builder);
        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
        builder.setTaskSize(taskSize);
        return taskSnapshot;
@@ -397,11 +398,11 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
        final SnapshotDrawerUtils.SystemBarBackgroundPainter
                decorPainter = new SnapshotDrawerUtils.SystemBarBackgroundPainter(attrs.flags,
                attrs.privateFlags, attrs.insetsFlags.appearance, taskDescription,
                mHighResTaskSnapshotScale, mainWindow.getRequestedVisibleTypes());
                mHighResSnapshotScale, mainWindow.getRequestedVisibleTypes());
        final int taskWidth = taskBounds.width();
        final int taskHeight = taskBounds.height();
        final int width = (int) (taskWidth * mHighResTaskSnapshotScale);
        final int height = (int) (taskHeight * mHighResTaskSnapshotScale);
        final int width = (int) (taskWidth * mHighResSnapshotScale);
        final int height = (int) (taskHeight * mHighResSnapshotScale);
        final RenderNode node = RenderNode.create("SnapshotController", null);
        node.setLeftTopRightBottom(0, 0, width, height);
        node.setClipToBounds(false);
@@ -450,9 +451,28 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
        return 0;
    }

    /**
     * Called when an {@link ActivityRecord} has been removed.
     */
    void onAppRemoved(ActivityRecord activity) {
        mCache.onAppRemoved(activity);
    }

    /**
     * Called when the process of an {@link ActivityRecord} has died.
     */
    void onAppDied(ActivityRecord activity) {
        mCache.onAppDied(activity);
    }

    boolean isAnimatingByRecents(@NonNull Task task) {
        return task.isAnimatingByRecents()
                || mService.mAtmService.getTransitionController().inRecentsTransition(task);
    }

    void dump(PrintWriter pw, String prefix) {
        pw.println(prefix + "mHighResTaskSnapshotScale=" + mHighResTaskSnapshotScale);
        pw.println(prefix + "mTaskSnapshotEnabled=" + mSnapshotEnabled);
        pw.println(prefix + "mHighResSnapshotScale=" + mHighResSnapshotScale);
        pw.println(prefix + "mSnapshotEnabled=" + mSnapshotEnabled);
        mCache.dump(pw, prefix);
    }
}
+15 −2
Original line number Diff line number Diff line
@@ -4222,7 +4222,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A

        getDisplayContent().mOpeningApps.remove(this);
        getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
        mWmService.mTaskSnapshotController.onAppRemoved(this);
        mWmService.mSnapshotController.onAppRemoved(this);

        mTaskSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
        mTaskSupervisor.mStoppingActivities.remove(this);
        waitingToShow = false;
@@ -5573,7 +5574,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                && !fromTransition) {
            // Take the screenshot before possibly hiding the WSA, otherwise the screenshot
            // will not be taken.
            mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
            mWmService.mSnapshotController.notifyAppVisibilityChanged(this, visible);
        }

        // If we are hidden but there is no delay needed we immediately
@@ -10513,6 +10514,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                && !inPinnedWindowingMode() && !inFreeformWindowingMode();
    }

    boolean canCaptureSnapshot() {
        if (!isSurfaceShowing() || findMainWindow() == null) {
            return false;
        }
        return forAllWindows(
                // Ensure at least one window for the top app is visible before attempting to
                // take a screenshot. Visible here means that the WSA surface is shown and has
                // an alpha greater than 0.
                ws -> ws.mWinAnimator != null && ws.mWinAnimator.getShown()
                        && ws.mWinAnimator.mLastAlpha > 0f, true  /* traverseTopToBottom */);
    }

    void overrideCustomTransition(boolean open, int enterAnim, int exitAnim, int backgroundColor) {
        CustomAppTransition transition = getCustomAnimation(open);
        if (transition == null) {
+40 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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 android.window.TaskSnapshot;

/**
 * A snapshot cache for activity, the token is the hashCode of the activity.
 */
class ActivitySnapshotCache extends SnapshotCache<ActivityRecord> {

    ActivitySnapshotCache(WindowManagerService service) {
        super(service, "Activity");
    }

    @Override
    void putSnapshot(ActivityRecord ar, TaskSnapshot snapshot) {
        final int hasCode = System.identityHashCode(ar);
        final CacheEntry entry = mRunningCache.get(hasCode);
        if (entry != null) {
            mAppIdMap.remove(entry.topApp);
        }
        mAppIdMap.put(ar, hasCode);
        mRunningCache.put(hasCode, new CacheEntry(snapshot, ar));
    }
}
Loading