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

Commit ab322f85 authored by Xiang Wang's avatar Xiang Wang
Browse files

Provide game task bitmap to TakeScreenshotService as screenshot

* Move the BitmapUtil to com.android.internal package
* Remove bitmap result from GameSession#takeScreenshot API

Bug: 219992742
Test: atest GameServiceProviderInstanceImplTest
Change-Id: I4bf29d623f781434ec7ffe4443e658880c31e619
Move the BitmapUtil to com.android.internal package
parent c4ab8175
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -11402,7 +11402,7 @@ package android.service.games {
  public static interface GameSession.ScreenshotCallback {
    method public void onFailure(int);
    method public void onSuccess(@NonNull android.graphics.Bitmap);
    method public void onSuccess();
    field public static final int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 0; // 0x0
  }
+11 −41
Original line number Diff line number Diff line
@@ -18,8 +18,6 @@ package android.service.games;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;

@@ -30,9 +28,7 @@ import java.util.Objects;
/**
 * Result object for calls to {@link IGameSessionController#takeScreenshot}.
 *
 * It includes a status (see {@link #getStatus}) and, if the status is
 * {@link #GAME_SCREENSHOT_SUCCESS} an {@link android.graphics.Bitmap} result (see {@link
 * #getBitmap}).
 * It includes a status only (see {@link #getStatus}).
 *
 * @hide
 */
@@ -54,8 +50,7 @@ public final class GameScreenshotResult implements Parcelable {

    /**
     * Indicates that the result of a call to {@link IGameSessionController#takeScreenshot} was
     * successful and an {@link android.graphics.Bitmap} result should be available by calling
     * {@link #getBitmap}.
     * successful.
     *
     * @hide
     */
@@ -81,9 +76,7 @@ public final class GameScreenshotResult implements Parcelable {
            new Parcelable.Creator<GameScreenshotResult>() {
                @Override
                public GameScreenshotResult createFromParcel(Parcel source) {
                    return new GameScreenshotResult(
                            source.readInt(),
                            source.readParcelable(null, Bitmap.class));
                    return new GameScreenshotResult(source.readInt());
                }

                @Override
@@ -95,14 +88,11 @@ public final class GameScreenshotResult implements Parcelable {
    @GameScreenshotStatus
    private final int mStatus;

    @Nullable
    private final Bitmap mBitmap;

    /**
     * Creates a successful {@link GameScreenshotResult} with the provided bitmap.
     * Creates a successful {@link GameScreenshotResult}.
     */
    public static GameScreenshotResult createSuccessResult(@NonNull Bitmap bitmap) {
        return new GameScreenshotResult(GAME_SCREENSHOT_SUCCESS, bitmap);
    public static GameScreenshotResult createSuccessResult() {
        return new GameScreenshotResult(GAME_SCREENSHOT_SUCCESS);
    }

    /**
@@ -110,12 +100,11 @@ public final class GameScreenshotResult implements Parcelable {
     * {@link #GAME_SCREENSHOT_ERROR_INTERNAL_ERROR} status.
     */
    public static GameScreenshotResult createInternalErrorResult() {
        return new GameScreenshotResult(GAME_SCREENSHOT_ERROR_INTERNAL_ERROR, null);
        return new GameScreenshotResult(GAME_SCREENSHOT_ERROR_INTERNAL_ERROR);
    }

    private GameScreenshotResult(@GameScreenshotStatus int status, @Nullable Bitmap bitmap) {
    private GameScreenshotResult(@GameScreenshotStatus int status) {
        this.mStatus = status;
        this.mBitmap = bitmap;
    }

    @Override
@@ -126,7 +115,6 @@ public final class GameScreenshotResult implements Parcelable {
    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeInt(mStatus);
        dest.writeParcelable(mBitmap, flags);
    }

    @GameScreenshotStatus
@@ -134,29 +122,12 @@ public final class GameScreenshotResult implements Parcelable {
        return mStatus;
    }

    /**
     * Gets the {@link Bitmap} result from a successful screenshot attempt.
     *
     * @return The bitmap.
     * @throws IllegalStateException if this method is called when {@link #getStatus} does not
     *                               return {@link #GAME_SCREENSHOT_SUCCESS}.
     */
    @NonNull
    public Bitmap getBitmap() {
        if (mBitmap == null) {
            throw new IllegalStateException("Bitmap not available for failed screenshot result");
        }
        return mBitmap;
    }

    @Override
    public String toString() {
        return "GameScreenshotResult{"
                + "mStatus="
                + mStatus
                + ", has bitmap='"
                + mBitmap != null ? "yes" : "no"
                + "\'}";
                + "}";
    }

    @Override
@@ -170,12 +141,11 @@ public final class GameScreenshotResult implements Parcelable {
        }

        GameScreenshotResult that = (GameScreenshotResult) o;
        return mStatus == that.mStatus
                && Objects.equals(mBitmap, that.mBitmap);
        return mStatus == that.mStatus;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mStatus, mBitmap);
        return Objects.hash(mStatus);
    }
}
+5 −8
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
@@ -367,7 +366,7 @@ public abstract class GameSession {
    }

    /**
     * Interface for returning screenshot outcome from calls to {@link #takeScreenshot}.
     * Interface for handling result of {@link #takeScreenshot}.
     */
    public interface ScreenshotCallback {

@@ -402,18 +401,16 @@ public abstract class GameSession {

        /**
         * Called when taking the screenshot succeeded.
         *
         * @param bitmap The screenshot.
         */
        void onSuccess(@NonNull Bitmap bitmap);
        void onSuccess();
    }

    /**
     * Takes a screenshot of the associated game. For this call to succeed, the device screen
     * must be turned on and the game task must be visible.
     *
     * If the callback is called with {@link ScreenshotCallback#onSuccess}, the provided {@link
     * Bitmap} may be used.
     * If the callback is called with {@link ScreenshotCallback#onSuccess}, the screenshot is
     * taken successfully.
     *
     * If the callback is called with {@link ScreenshotCallback#onFailure}, the provided status
     * code should be checked.
@@ -460,7 +457,7 @@ public abstract class GameSession {
        @GameScreenshotResult.GameScreenshotStatus int status = result.getStatus();
        switch (status) {
            case GameScreenshotResult.GAME_SCREENSHOT_SUCCESS:
                callback.onSuccess(result.getBitmap());
                callback.onSuccess();
                break;
            case GameScreenshotResult.GAME_SCREENSHOT_ERROR_INTERNAL_ERROR:
                Slog.w(TAG, "Error taking screenshot");
+71 −0
Original line number Diff line number Diff line
@@ -11,8 +11,12 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.graphics.ColorSpace;
import android.graphics.Insets;
import android.graphics.ParcelableColorSpace;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -26,6 +30,7 @@ import android.os.UserHandle;
import android.util.Log;
import android.view.WindowManager;

import java.util.Objects;
import java.util.function.Consumer;

public class ScreenshotHelper {
@@ -154,6 +159,72 @@ public class ScreenshotHelper {
                };
    }

    /**
     * Bundler used to convert between a hardware bitmap and a bundle without copying the internal
     * content. This is expected to be used together with {@link #provideScreenshot} to handle a
     * hardware bitmap as a screenshot.
     */
    public static final class HardwareBitmapBundler {
        private static final String KEY_BUFFER = "bitmap_util_buffer";
        private static final String KEY_COLOR_SPACE = "bitmap_util_color_space";

        private HardwareBitmapBundler() {
        }

        /**
         * Creates a Bundle that represents the given Bitmap.
         * <p>The Bundle will contain a wrapped version of the Bitmaps HardwareBuffer, so will avoid
         * copies when passing across processes, only pass to processes you trust.
         *
         * <p>Returns a new Bundle rather than modifying an exiting one to avoid key collisions, the
         * returned Bundle should be treated as a standalone object.
         *
         * @param bitmap to convert to bundle
         * @return a Bundle representing the bitmap, should only be parsed by
         * {@link #bundleToHardwareBitmap(Bundle)}
         */
        public static Bundle hardwareBitmapToBundle(Bitmap bitmap) {
            if (bitmap.getConfig() != Bitmap.Config.HARDWARE) {
                throw new IllegalArgumentException(
                        "Passed bitmap must have hardware config, found: " + bitmap.getConfig());
            }

            // Bitmap assumes SRGB for null color space
            ParcelableColorSpace colorSpace =
                    bitmap.getColorSpace() == null
                            ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB))
                            : new ParcelableColorSpace(bitmap.getColorSpace());

            Bundle bundle = new Bundle();
            bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer());
            bundle.putParcelable(KEY_COLOR_SPACE, colorSpace);

            return bundle;
        }

        /**
         * Extracts the Bitmap added to a Bundle with {@link #hardwareBitmapToBundle(Bitmap)} .}
         *
         * <p>This Bitmap contains the HardwareBuffer from the original caller, be careful passing
         * this
         * Bitmap on to any other source.
         *
         * @param bundle containing the bitmap
         * @return a hardware Bitmap
         */
        public static Bitmap bundleToHardwareBitmap(Bundle bundle) {
            if (!bundle.containsKey(KEY_BUFFER) || !bundle.containsKey(KEY_COLOR_SPACE)) {
                throw new IllegalArgumentException("Bundle does not contain a hardware bitmap");
            }

            HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER);
            ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE);

            return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer),
                    colorSpace.getColorSpace());
        }
    }

    private static final String TAG = "ScreenshotHelper";

    // Time until we give up on the screenshot & show an error instead.
+0 −87
Original line number Diff line number Diff line
/*
 * 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 com.android.systemui.shared.recents.utilities;

import android.graphics.Bitmap;
import android.graphics.ColorSpace;
import android.graphics.ParcelableColorSpace;
import android.hardware.HardwareBuffer;
import android.os.Bundle;

import java.util.Objects;

/**
 * Utils for working with Bitmaps.
 */
public final class BitmapUtil {
    private static final String KEY_BUFFER = "bitmap_util_buffer";
    private static final String KEY_COLOR_SPACE = "bitmap_util_color_space";

    private BitmapUtil(){ }

    /**
     * Creates a Bundle that represents the given Bitmap.
     * <p>The Bundle will contain a wrapped version of the Bitmaps HardwareBuffer, so will avoid
     * copies when passing across processes, only pass to processes you trust.
     *
     * <p>Returns a new Bundle rather than modifying an exiting one to avoid key collisions, the
     * returned Bundle should be treated as a standalone object.
     *
     * @param bitmap to convert to bundle
     * @return a Bundle representing the bitmap, should only be parsed by
     *         {@link #bundleToHardwareBitmap(Bundle)}
     */
    public static Bundle hardwareBitmapToBundle(Bitmap bitmap) {
        if (bitmap.getConfig() != Bitmap.Config.HARDWARE) {
            throw new IllegalArgumentException(
                    "Passed bitmap must have hardware config, found: " + bitmap.getConfig());
        }

        // Bitmap assumes SRGB for null color space
        ParcelableColorSpace colorSpace =
                bitmap.getColorSpace() == null
                        ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB))
                        : new ParcelableColorSpace(bitmap.getColorSpace());

        Bundle bundle = new Bundle();
        bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer());
        bundle.putParcelable(KEY_COLOR_SPACE, colorSpace);

        return bundle;
    }

    /**
     * Extracts the Bitmap added to a Bundle with {@link #hardwareBitmapToBundle(Bitmap)} .}
     *
     * <p>This Bitmap contains the HardwareBuffer from the original caller, be careful passing this
     * Bitmap on to any other source.
     *
     * @param bundle containing the bitmap
     * @return a hardware Bitmap
     */
    public static Bitmap bundleToHardwareBitmap(Bundle bundle) {
        if (!bundle.containsKey(KEY_BUFFER) || !bundle.containsKey(KEY_COLOR_SPACE)) {
            throw new IllegalArgumentException("Bundle does not contain a hardware bitmap");
        }

        HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER);
        ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE);

        return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer),
                colorSpace.getColorSpace());
    }
}
Loading