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

Commit 76439d83 authored by Chavi Weingarten's avatar Chavi Weingarten Committed by Android (Google) Code Review
Browse files

Merge "Fix wallpaper screenshot" into pi-dev

parents 04f2800d 0315a1a8
Loading
Loading
Loading
Loading
+38 −68
Original line number Diff line number Diff line
@@ -137,7 +137,6 @@ import android.os.SystemClock;
import android.os.Trace;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.MutableBoolean;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -156,7 +155,6 @@ import com.android.internal.view.IInputMethodClient;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.RotationCache;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Comparator;
@@ -2960,10 +2958,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
     * In portrait mode, it grabs the full screenshot.
     *
     * @param config of the output bitmap
     * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
     */
    Bitmap screenshotDisplay(Bitmap.Config config, boolean wallpaperOnly) {
        synchronized (mService.mWindowMap) {
    Bitmap screenshotDisplayLocked(Bitmap.Config config) {
        if (!mService.mPolicy.isScreenOn()) {
            if (DEBUG_SCREENSHOT) {
                Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
@@ -2971,10 +2967,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
            return null;
        }

            if (wallpaperOnly && !shouldScreenshotWallpaper()) {
                return null;
            }

        int dw = mDisplayInfo.logicalWidth;
        int dh = mDisplayInfo.logicalHeight;

@@ -3016,28 +3008,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        bitmap.recycle();
        return ret;
    }
    }

    private boolean shouldScreenshotWallpaper() {
        MutableBoolean screenshotReady = new MutableBoolean(false);

        forAllWindows(w -> {
            if (!w.mIsWallpaper) {
                return false;
            }

            // Found the wallpaper window
            final WindowStateAnimator winAnim = w.mWinAnimator;

            if (winAnim.getShown() && winAnim.mLastAlpha > 0f) {
                screenshotReady.value = true;
            }

            return true;
        }, true /* traverseTopToBottom */);

        return screenshotReady.value;
    }

    // TODO: Can this use createRotationMatrix()?
    private static void convertCropForSurfaceFlinger(Rect crop, int rot, int dw, int dh) {
+63 −0
Original line number Diff line number Diff line
@@ -27,12 +27,16 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.WALLPAPER_DRAW_PENDING_TIMEOUT;

import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Debug;
import android.os.IBinder;
@@ -41,6 +45,7 @@ import android.os.SystemClock;
import android.util.ArraySet;
import android.util.Slog;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.animation.Animation;

@@ -95,6 +100,12 @@ class WallpaperController {
    private static final int WALLPAPER_DRAW_TIMEOUT = 2;
    private int mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;

    /**
     * Temporary storage for taking a screenshot of the wallpaper.
     * @see #screenshotWallpaperLocked()
     */
    private WindowState mTmpTopWallpaper;

    private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult();

    private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> {
@@ -679,6 +690,58 @@ class WallpaperController {
        mWallpaperTokens.remove(token);
    }

    /**
     * Take a screenshot of the wallpaper if it's visible.
     *
     * @return Bitmap of the wallpaper
     */
    Bitmap screenshotWallpaperLocked() {
        if (!mService.mPolicy.isScreenOn()) {
            if (DEBUG_SCREENSHOT) {
                Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
            }
            return null;
        }

        final WindowState wallpaperWindowState = getTopVisibleWallpaper();
        if (wallpaperWindowState == null) {
            if (DEBUG_SCREENSHOT) {
                Slog.i(TAG_WM, "No visible wallpaper to screenshot");
            }
            return null;
        }

        final Rect bounds = wallpaperWindowState.getBounds();
        bounds.offsetTo(0, 0);

        GraphicBuffer wallpaperBuffer = SurfaceControl.captureLayers(
                wallpaperWindowState.getSurfaceControl().getHandle(), bounds, 1 /* frameScale */);

        if (wallpaperBuffer == null) {
            Slog.w(TAG_WM, "Failed to screenshot wallpaper");
            return null;
        }
        return Bitmap.createHardwareBitmap(wallpaperBuffer);
    }

    private WindowState getTopVisibleWallpaper() {
        mTmpTopWallpaper = null;

        for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
            final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
            token.forAllWindows(w -> {
                final WindowStateAnimator winAnim = w.mWinAnimator;
                if (winAnim != null && winAnim.getShown() && winAnim.mLastAlpha > 0f) {
                    mTmpTopWallpaper = w;
                    return true;
                }
                return false;
            }, true /* traverseTopToBottom */);
        }

        return mTmpTopWallpaper;
    }

    void dump(PrintWriter pw, String prefix) {
        pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
        if (mPrevWallpaperTarget != null) {
+19 −32
Original line number Diff line number Diff line
@@ -28,8 +28,6 @@ import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_USER_HANDLE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.ROOT_UID;
import static android.os.Process.SHELL_UID;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -3607,14 +3605,14 @@ public class WindowManagerService extends IWindowManager.Stub

    @Override
    public Bitmap screenshotWallpaper() {
        if (!checkCallingPermission(READ_FRAME_BUFFER,
                "screenshotWallpaper()")) {
        if (!checkCallingPermission(READ_FRAME_BUFFER, "screenshotWallpaper()")) {
            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
        }
        try {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
            return screenshotApplications(DEFAULT_DISPLAY, Bitmap.Config.ARGB_8888,
                    true /* wallpaperOnly */);
            synchronized (mWindowMap) {
                return mRoot.mWallpaperController.screenshotWallpaperLocked();
            }
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
@@ -3627,14 +3625,25 @@ public class WindowManagerService extends IWindowManager.Stub
     */
    @Override
    public boolean requestAssistScreenshot(final IAssistDataReceiver receiver) {
        if (!checkCallingPermission(READ_FRAME_BUFFER,
                "requestAssistScreenshot()")) {
        if (!checkCallingPermission(READ_FRAME_BUFFER, "requestAssistScreenshot()")) {
            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
        }

        final Bitmap bm;
        synchronized (mWindowMap) {
            final DisplayContent displayContent = mRoot.getDisplayContent(DEFAULT_DISPLAY);
            if (displayContent == null) {
                if (DEBUG_SCREENSHOT) {
                    Slog.i(TAG_WM, "Screenshot returning null. No Display for displayId="
                            + DEFAULT_DISPLAY);
                }
                bm = null;
            } else {
                bm = displayContent.screenshotDisplayLocked(Bitmap.Config.ARGB_8888);
            }
        }

        FgThread.getHandler().post(() -> {
            Bitmap bm = screenshotApplications(DEFAULT_DISPLAY, Bitmap.Config.ARGB_8888,
                    false /* wallpaperOnly */);
            try {
                receiver.onHandleAssistScreenshot(bm);
            } catch (RemoteException e) {
@@ -3663,28 +3672,6 @@ public class WindowManagerService extends IWindowManager.Stub
        }
    }

    /**
     * Takes a snapshot of the screen.  In landscape mode this grabs the whole screen.
     * In portrait mode, it grabs the full screenshot.
     *
     * @param displayId the Display to take a screenshot of.
     * @param config of the output bitmap
     * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
     */
    private Bitmap screenshotApplications(int displayId, Bitmap.Config config,
            boolean wallpaperOnly) {
        final DisplayContent displayContent;
        synchronized(mWindowMap) {
            displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot returning null. No Display for "
                        + "displayId=" + displayId);
                return null;
            }
        }
        return displayContent.screenshotDisplay(config, wallpaperOnly);
    }

    /**
     * Freeze rotation changes.  (Enable "rotation lock".)
     * Persists across reboots.
+67 −0
Original line number Diff line number Diff line
package com.android.server.wm;

import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;

import static junit.framework.TestCase.assertNotNull;

import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.graphics.Bitmap;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * Tests for the {@link WallpaperController} class.
 *
 * Build/Install/Run:
 *  atest com.android.server.wm.WallpaperControllerTests
 */
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
public class WallpaperControllerTests extends WindowTestsBase {
    @Test
    public void testWallpaperScreenshot() {
        WindowSurfaceController windowSurfaceController = mock(WindowSurfaceController.class);

        synchronized (sWm.mWindowMap) {
            // No wallpaper
            final DisplayContent dc = createNewDisplay();
            Bitmap wallpaperBitmap = sWm.mRoot.mWallpaperController.screenshotWallpaperLocked();
            assertNull(wallpaperBitmap);

            // No wallpaper WSA Surface
            WindowToken wallpaperWindowToken = new WallpaperWindowToken(sWm, mock(IBinder.class),
                    true, dc, true /* ownerCanManageAppTokens */);
            WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER,
                    wallpaperWindowToken, "wallpaperWindow");
            wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
            assertNull(wallpaperBitmap);

            // Wallpaper with not visible WSA surface.
            wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController;
            wallpaperWindow.mWinAnimator.mLastAlpha = 1;
            wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
            assertNull(wallpaperBitmap);

            when(windowSurfaceController.getShown()).thenReturn(true);

            // Wallpaper with WSA alpha set to 0.
            wallpaperWindow.mWinAnimator.mLastAlpha = 0;
            wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
            assertNull(wallpaperBitmap);

            // Wallpaper window with WSA Surface
            wallpaperWindow.mWinAnimator.mLastAlpha = 1;
            wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
            assertNotNull(wallpaperBitmap);
        }
    }
}
+4 −1
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ class WindowTestsBase {
    WindowState mChildAppWindowAbove;
    WindowState mChildAppWindowBelow;
    HashSet<WindowState> mCommonWindows;
    WallpaperController mWallpaperController;

    @Mock
    static WindowState.PowerManagerWrapper mPowerManagerWrapper;
@@ -105,6 +106,8 @@ class WindowTestsBase {
        sWm = TestWindowManagerPolicy.getWindowManagerService(context);
        beforeCreateDisplay();

        mWallpaperController = new WallpaperController(sWm);

        context.getDisplay().getDisplayInfo(mDisplayInfo);
        mDisplayContent = createNewDisplay();
        sWm.mDisplayEnabled = true;
@@ -293,7 +296,7 @@ class WindowTestsBase {
        final int displayId = sNextDisplayId++;
        final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                mDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
        return new DisplayContent(display, sWm, new WallpaperController(sWm),
        return new DisplayContent(display, sWm, mWallpaperController,
                mock(DisplayWindowController.class));
    }