/* * Copyright (C) 2020 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 android.window; import android.annotation.IntDef; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.res.Configuration; import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.Point; import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; import android.view.Surface; import android.view.WindowInsetsController; import com.android.window.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Represents a task snapshot. * @hide */ public class TaskSnapshot implements Parcelable { // Identifier of this snapshot private final long mId; // The elapsed real time (in nanoseconds) when this snapshot was captured, not intended for use outside the // process in which the snapshot was taken (ie. this is not parceled) private final long mCaptureTime; // Top activity in task when snapshot was taken private final ComponentName mTopActivityComponent; private final HardwareBuffer mSnapshot; /** Indicates whether task was in landscape or portrait */ @Configuration.Orientation private final int mOrientation; /** See {@link android.view.Surface.Rotation} */ @Surface.Rotation private final int mRotation; /** The size of the snapshot before scaling */ private final Point mTaskSize; private final Rect mContentInsets; private final Rect mLetterboxInsets; // Whether this snapshot is a down-sampled version of the high resolution snapshot, used // mainly for loading snapshots quickly from disk when user is flinging fast private final boolean mIsLowResolution; // Whether or not the snapshot is a real snapshot or an app-theme generated snapshot due to // the task having a secure window or having previews disabled private final boolean mIsRealSnapshot; private final int mWindowingMode; private final @WindowInsetsController.Appearance int mAppearance; private final boolean mIsTranslucent; private final boolean mHasImeSurface; // Must be one of the named color spaces, otherwise, always use SRGB color space. private final ColorSpace mColorSpace; private int mInternalReferences; /** This snapshot object is being broadcast. */ public static final int REFERENCE_BROADCAST = 1; /** This snapshot object is in the cache. */ public static final int REFERENCE_CACHE = 1 << 1; /** This snapshot object is being persistent. */ public static final int REFERENCE_PERSIST = 1 << 2; @IntDef(flag = true, prefix = { "REFERENCE_" }, value = { REFERENCE_BROADCAST, REFERENCE_CACHE, REFERENCE_PERSIST }) @Retention(RetentionPolicy.SOURCE) @interface ReferenceFlags {} public TaskSnapshot(long id, long captureTime, @NonNull ComponentName topActivityComponent, HardwareBuffer snapshot, @NonNull ColorSpace colorSpace, int orientation, int rotation, Point taskSize, Rect contentInsets, Rect letterboxInsets, boolean isLowResolution, boolean isRealSnapshot, int windowingMode, @WindowInsetsController.Appearance int appearance, boolean isTranslucent, boolean hasImeSurface) { mId = id; mCaptureTime = captureTime; mTopActivityComponent = topActivityComponent; mSnapshot = snapshot; mColorSpace = colorSpace.getId() < 0 ? ColorSpace.get(ColorSpace.Named.SRGB) : colorSpace; mOrientation = orientation; mRotation = rotation; mTaskSize = new Point(taskSize); mContentInsets = new Rect(contentInsets); mLetterboxInsets = new Rect(letterboxInsets); mIsLowResolution = isLowResolution; mIsRealSnapshot = isRealSnapshot; mWindowingMode = windowingMode; mAppearance = appearance; mIsTranslucent = isTranslucent; mHasImeSurface = hasImeSurface; } private TaskSnapshot(Parcel source) { mId = source.readLong(); mCaptureTime = SystemClock.elapsedRealtimeNanos(); mTopActivityComponent = ComponentName.readFromParcel(source); mSnapshot = source.readTypedObject(HardwareBuffer.CREATOR); int colorSpaceId = source.readInt(); mColorSpace = colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length ? ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]) : ColorSpace.get(ColorSpace.Named.SRGB); mOrientation = source.readInt(); mRotation = source.readInt(); mTaskSize = source.readTypedObject(Point.CREATOR); mContentInsets = source.readTypedObject(Rect.CREATOR); mLetterboxInsets = source.readTypedObject(Rect.CREATOR); mIsLowResolution = source.readBoolean(); mIsRealSnapshot = source.readBoolean(); mWindowingMode = source.readInt(); mAppearance = source.readInt(); mIsTranslucent = source.readBoolean(); mHasImeSurface = source.readBoolean(); } /** * @return Identifier of this snapshot. */ public long getId() { return mId; } /** * @return The elapsed real time (in nanoseconds) when this snapshot was captured. This time is * only valid in the process where this snapshot was taken. */ public long getCaptureTime() { return mCaptureTime; } /** * @return The top activity component for the task at the point this snapshot was taken. */ public ComponentName getTopActivityComponent() { return mTopActivityComponent; } /** * @return The graphic buffer representing the screenshot. * * Note: Prefer {@link #getHardwareBuffer}, which returns the internal object. This version * creates a new object. */ @UnsupportedAppUsage public GraphicBuffer getSnapshot() { return GraphicBuffer.createFromHardwareBuffer(mSnapshot); } /** * @return The hardware buffer representing the screenshot. */ public HardwareBuffer getHardwareBuffer() { return mSnapshot; } /** * @return The color space of hardware buffer representing the screenshot. */ public ColorSpace getColorSpace() { return mColorSpace; } /** * @return The screen orientation the screenshot was taken in. */ @UnsupportedAppUsage public int getOrientation() { return mOrientation; } /** * @return The screen rotation the screenshot was taken in. */ public int getRotation() { return mRotation; } /** * @return The size of the task at the point this snapshot was taken. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public Point getTaskSize() { return mTaskSize; } /** * @return The system/content insets on the snapshot. These can be clipped off in order to * remove any areas behind system bars in the snapshot. */ @UnsupportedAppUsage public Rect getContentInsets() { return mContentInsets; } /** * @return The letterbox insets on the snapshot. These can be clipped off in order to * remove any letterbox areas in the snapshot. */ public Rect getLetterboxInsets() { return mLetterboxInsets; } /** * @return Whether this snapshot is a down-sampled version of the full resolution. */ @UnsupportedAppUsage public boolean isLowResolution() { return mIsLowResolution; } /** * @return Whether or not the snapshot is a real snapshot or an app-theme generated snapshot * due to the task having a secure window or having previews disabled. */ @UnsupportedAppUsage public boolean isRealSnapshot() { return mIsRealSnapshot; } /** * @return Whether or not the snapshot is of a translucent app window (non-fullscreen or has * a non-opaque pixel format). */ public boolean isTranslucent() { return mIsTranslucent; } /** * @return Whether or not the snapshot has the IME surface. */ public boolean hasImeSurface() { return mHasImeSurface; } /** * @return The windowing mode of the task when this snapshot was taken. */ public int getWindowingMode() { return mWindowingMode; } /** * @return The {@link WindowInsetsController.Appearance} flags for the top most visible * fullscreen window at the time that the snapshot was taken. */ public @WindowInsetsController.Appearance int getAppearance() { return mAppearance; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(mId); ComponentName.writeToParcel(mTopActivityComponent, dest); dest.writeTypedObject(mSnapshot != null && !mSnapshot.isClosed() ? mSnapshot : null, 0); dest.writeInt(mColorSpace.getId()); dest.writeInt(mOrientation); dest.writeInt(mRotation); dest.writeTypedObject(mTaskSize, 0); dest.writeTypedObject(mContentInsets, 0); dest.writeTypedObject(mLetterboxInsets, 0); dest.writeBoolean(mIsLowResolution); dest.writeBoolean(mIsRealSnapshot); dest.writeInt(mWindowingMode); dest.writeInt(mAppearance); dest.writeBoolean(mIsTranslucent); dest.writeBoolean(mHasImeSurface); } @Override public String toString() { final int width = mSnapshot != null ? mSnapshot.getWidth() : 0; final int height = mSnapshot != null ? mSnapshot.getHeight() : 0; return "TaskSnapshot{" + " mId=" + mId + " mCaptureTime=" + mCaptureTime + " mTopActivityComponent=" + mTopActivityComponent.flattenToShortString() + " mSnapshot=" + mSnapshot + " (" + width + "x" + height + ")" + " mColorSpace=" + mColorSpace.toString() + " mOrientation=" + mOrientation + " mRotation=" + mRotation + " mTaskSize=" + mTaskSize.toString() + " mContentInsets=" + mContentInsets.toShortString() + " mLetterboxInsets=" + mLetterboxInsets.toShortString() + " mIsLowResolution=" + mIsLowResolution + " mIsRealSnapshot=" + mIsRealSnapshot + " mWindowingMode=" + mWindowingMode + " mAppearance=" + mAppearance + " mIsTranslucent=" + mIsTranslucent + " mHasImeSurface=" + mHasImeSurface + " mInternalReferences=" + mInternalReferences; } /** * Adds a reference when the object is held somewhere. * Only used in core. */ public synchronized void addReference(@ReferenceFlags int usage) { mInternalReferences |= usage; } /** * Removes a reference when the object is not held from somewhere. The snapshot will be closed * once the reference becomes zero. * Only used in core. */ public synchronized void removeReference(@ReferenceFlags int usage) { mInternalReferences &= ~usage; if (Flags.releaseSnapshotAggressively() && mInternalReferences == 0 && mSnapshot != null && !mSnapshot.isClosed()) { mSnapshot.close(); } } public static final @NonNull Creator CREATOR = new Creator() { public TaskSnapshot createFromParcel(Parcel source) { return new TaskSnapshot(source); } public TaskSnapshot[] newArray(int size) { return new TaskSnapshot[size]; } }; /** Builder for a {@link TaskSnapshot} object */ public static final class Builder { private long mId; private long mCaptureTime; private ComponentName mTopActivity; private HardwareBuffer mSnapshot; private ColorSpace mColorSpace; private int mOrientation; private int mRotation; private Point mTaskSize; private Rect mContentInsets; private Rect mLetterboxInsets; private boolean mIsRealSnapshot; private int mWindowingMode; private @WindowInsetsController.Appearance int mAppearance; private boolean mIsTranslucent; private boolean mHasImeSurface; private int mPixelFormat; public Builder setId(long id) { mId = id; return this; } public Builder setCaptureTime(long captureTime) { mCaptureTime = captureTime; return this; } public Builder setTopActivityComponent(ComponentName name) { mTopActivity = name; return this; } public Builder setSnapshot(HardwareBuffer buffer) { mSnapshot = buffer; return this; } public Builder setColorSpace(ColorSpace colorSpace) { mColorSpace = colorSpace; return this; } public Builder setOrientation(int orientation) { mOrientation = orientation; return this; } public Builder setRotation(int rotation) { mRotation = rotation; return this; } /** * Sets the original size of the task */ public Builder setTaskSize(Point size) { mTaskSize = size; return this; } public Builder setContentInsets(Rect contentInsets) { mContentInsets = contentInsets; return this; } public Builder setLetterboxInsets(Rect letterboxInsets) { mLetterboxInsets = letterboxInsets; return this; } public Builder setIsRealSnapshot(boolean realSnapshot) { mIsRealSnapshot = realSnapshot; return this; } public Builder setWindowingMode(int windowingMode) { mWindowingMode = windowingMode; return this; } public Builder setAppearance(@WindowInsetsController.Appearance int appearance) { mAppearance = appearance; return this; } public Builder setIsTranslucent(boolean isTranslucent) { mIsTranslucent = isTranslucent; return this; } /** * Sets the IME visibility when taking the snapshot of the task. */ public Builder setHasImeSurface(boolean hasImeSurface) { mHasImeSurface = hasImeSurface; return this; } public int getPixelFormat() { return mPixelFormat; } public Builder setPixelFormat(int pixelFormat) { mPixelFormat = pixelFormat; return this; } public TaskSnapshot build() { return new TaskSnapshot( mId, mCaptureTime, mTopActivity, mSnapshot, mColorSpace, mOrientation, mRotation, mTaskSize, mContentInsets, mLetterboxInsets, // When building a TaskSnapshot with the Builder class, isLowResolution // is always false. Low-res snapshots are only created when loading from // disk. false /* isLowResolution */, mIsRealSnapshot, mWindowingMode, mAppearance, mIsTranslucent, mHasImeSurface); } } }