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

Commit 032eae98 authored by Naomi Musgrave's avatar Naomi Musgrave Committed by Android (Google) Code Review
Browse files

Merge "Refactor content recording out of DisplayContent" into tm-dev

parents 33027975 472e00b0
Loading
Loading
Loading
Loading
+63 −63
Original line number Diff line number Diff line
@@ -295,6 +295,12 @@
      "group": "WM_DEBUG_STARTING_WINDOW",
      "at": "com\/android\/server\/wm\/ActivityRecord.java"
    },
    "-1781861035": {
      "message": "Display %d has content (%b) so pause recording",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/ContentRecorder.java"
    },
    "-1777196134": {
      "message": "goodToGo(): No apps to animate, mPendingAnimations=%d",
      "level": "DEBUG",
@@ -343,12 +349,6 @@
      "group": "WM_DEBUG_STATES",
      "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
    },
    "-1734445525": {
      "message": "Display %d has content (%b) so pause recording",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/DisplayContent.java"
    },
    "-1730156332": {
      "message": "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
      "level": "VERBOSE",
@@ -445,6 +445,12 @@
      "group": "WM_DEBUG_LOCKTASK",
      "at": "com\/android\/server\/wm\/LockTaskController.java"
    },
    "-1605829532": {
      "message": "Unable to start recording due to null token for display %d",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/ContentRecorder.java"
    },
    "-1598452494": {
      "message": "activityDestroyedLocked: r=%s",
      "level": "DEBUG",
@@ -511,12 +517,6 @@
      "group": "WM_ERROR",
      "at": "com\/android\/server\/wm\/WindowManagerService.java"
    },
    "-1542296596": {
      "message": "Going ahead with updating recording for display %d to new bounds %s and\/or orientation %d.",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/DisplayContent.java"
    },
    "-1539974875": {
      "message": "removeAppToken: %s delayed=%b Callers=%s",
      "level": "VERBOSE",
@@ -715,6 +715,12 @@
      "group": "WM_DEBUG_TASKS",
      "at": "com\/android\/server\/wm\/RootWindowContainer.java"
    },
    "-1373875178": {
      "message": "Going ahead with updating recording for display %d to new bounds %s and\/or orientation %d.",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/ContentRecorder.java"
    },
    "-1364754753": {
      "message": "Task vanished taskId=%d",
      "level": "VERBOSE",
@@ -739,11 +745,11 @@
      "group": "WM_DEBUG_STARTING_WINDOW",
      "at": "com\/android\/server\/wm\/ActivityRecord.java"
    },
    "-1323003327": {
      "message": "Unexpectedly null window container; unable to update recording for display %d",
    "-1326876381": {
      "message": "Provided surface for recording on display %d is not present, so do not update the surface",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/DisplayContent.java"
      "at": "com\/android\/server\/wm\/ContentRecorder.java"
    },
    "-1311436264": {
      "message": "Unregister task fragment organizer=%s uid=%d pid=%d",
@@ -859,12 +865,6 @@
      "group": "WM_DEBUG_WINDOW_INSETS",
      "at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
    },
    "-1179559337": {
      "message": "Unable to start recording due to invalid region for display %d",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/DisplayContent.java"
    },
    "-1176488860": {
      "message": "SURFACE isSecure=%b: %s",
      "level": "INFO",
@@ -1045,12 +1045,6 @@
      "group": "WM_DEBUG_DRAW",
      "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
    },
    "-992111757": {
      "message": "Unable to retrieve window container to start recording for display %d",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/DisplayContent.java"
    },
    "-986746907": {
      "message": "Starting window removed %s",
      "level": "DEBUG",
@@ -1111,12 +1105,6 @@
      "group": "WM_DEBUG_STATES",
      "at": "com\/android\/server\/wm\/ActivityRecord.java"
    },
    "-922507769": {
      "message": "Display %d has no content and is on, so start recording for state %d",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/DisplayContent.java"
    },
    "-917215012": {
      "message": "%s: caller %d is using old GET_TASKS but privileged; allowing",
      "level": "WARN",
@@ -1291,6 +1279,12 @@
      "group": "WM_DEBUG_SCREEN_ON",
      "at": "com\/android\/server\/wm\/WindowManagerService.java"
    },
    "-751255162": {
      "message": "Unable to update recording for display %d to new bounds %s and\/or orientation %d, since the surface is not available.",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/ContentRecorder.java"
    },
    "-743856570": {
      "message": "shouldWaitAnimatingExit: isAnimating: %s",
      "level": "DEBUG",
@@ -1303,6 +1297,12 @@
      "group": "WM_DEBUG_CONFIGURATION",
      "at": "com\/android\/server\/wm\/ActivityRecord.java"
    },
    "-732715767": {
      "message": "Unable to retrieve window container to start recording for display %d",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/ContentRecorder.java"
    },
    "-729530161": {
      "message": "Moving to DESTROYED: %s (no app)",
      "level": "VERBOSE",
@@ -1549,18 +1549,6 @@
      "group": "WM_DEBUG_KEEP_SCREEN_ON",
      "at": "com\/android\/server\/wm\/RootWindowContainer.java"
    },
    "-479990726": {
      "message": "Unable to start recording due to null token for display %d",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/DisplayContent.java"
    },
    "-473911359": {
      "message": "Display %d was already recording, so apply transformations if necessary",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/DisplayContent.java"
    },
    "-463348344": {
      "message": "Removing and adding activity %s to root task at top callers=%s",
      "level": "INFO",
@@ -1669,12 +1657,6 @@
      "group": "WM_DEBUG_TASKS",
      "at": "com\/android\/server\/wm\/RootWindowContainer.java"
    },
    "-370641936": {
      "message": "Unable to update recording for display %d to new bounds %s and\/or orientation %d, since the surface is not available.",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/DisplayContent.java"
    },
    "-360208282": {
      "message": "Animating wallpapers: old: %s hidden=%b new: %s hidden=%b",
      "level": "VERBOSE",
@@ -1747,6 +1729,12 @@
      "group": "WM_DEBUG_RECENTS_ANIMATIONS",
      "at": "com\/android\/server\/wm\/RecentsAnimation.java"
    },
    "-302468137": {
      "message": "Display %d was already recording, so apply transformations if necessary",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/ContentRecorder.java"
    },
    "-292790591": {
      "message": "Attempted to set IME policy to a display that does not exist: %d",
      "level": "WARN",
@@ -1873,6 +1861,12 @@
      "group": "WM_DEBUG_STATES",
      "at": "com\/android\/server\/wm\/Task.java"
    },
    "-142844021": {
      "message": "Unable to start recording for display %d since the surface is not available.",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/ContentRecorder.java"
    },
    "-134091882": {
      "message": "Screenshotting Activity %s",
      "level": "VERBOSE",
@@ -2605,6 +2599,12 @@
      "group": "WM_SHOW_TRANSACTIONS",
      "at": "com\/android\/server\/wm\/Session.java"
    },
    "609880497": {
      "message": "Display %d has no content and is on, so start recording for state %d",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/ContentRecorder.java"
    },
    "620368427": {
      "message": "******* TELLING SURFACE FLINGER WE ARE BOOTED!",
      "level": "INFO",
@@ -3319,6 +3319,12 @@
      "group": "WM_ERROR",
      "at": "com\/android\/server\/wm\/WindowManagerService.java"
    },
    "1444064727": {
      "message": "Unexpectedly null window container; unable to update recording for display %d",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/ContentRecorder.java"
    },
    "1448683958": {
      "message": "Override pending remote transitionSet=%b adapter=%s",
      "level": "INFO",
@@ -3445,6 +3451,12 @@
      "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
      "at": "com\/android\/server\/wm\/AppTransition.java"
    },
    "1608402305": {
      "message": "Unable to start recording due to invalid region for display %d",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/ContentRecorder.java"
    },
    "1610646518": {
      "message": "Enqueueing pending finish: %s",
      "level": "VERBOSE",
@@ -3547,12 +3559,6 @@
      "group": "WM_DEBUG_WINDOW_ORGANIZER",
      "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
    },
    "1707558369": {
      "message": "Unable to start recording for display %d since the surface is not available.",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/DisplayContent.java"
    },
    "1720229827": {
      "message": "Creating animation bounds layer",
      "level": "INFO",
@@ -3685,12 +3691,6 @@
      "group": "WM_DEBUG_STARTING_WINDOW",
      "at": "com\/android\/server\/wm\/ActivityRecord.java"
    },
    "1854279309": {
      "message": "Provided surface for recording on display %d is not present, so do not update the surface",
      "level": "VERBOSE",
      "group": "WM_DEBUG_CONTENT_RECORDING",
      "at": "com\/android\/server\/wm\/DisplayContent.java"
    },
    "1856211951": {
      "message": "moveFocusableActivityToTop: already on top, activity=%s",
      "level": "DEBUG",
+368 −0

File added.

Preview size limit exceeded, changes collapsed.

+20 −265

File changed.

Preview size limit exceeded, changes collapsed.

+239 −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 static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;

import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.VirtualDisplay;
import android.os.Binder;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
import android.view.ContentRecordingSession;
import android.view.Surface;
import android.view.SurfaceControl;

import androidx.test.filters.SmallTest;

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

/**
 * Tests for the {@link ContentRecorder} class.
 *
 * Build/Install/Run:
 *  atest WmTests:ContentRecorderTests
 */
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
public class ContentRecorderTests extends WindowTestsBase {
    private static final IBinder TEST_TOKEN = new RecordingTestToken();
    private final ContentRecordingSession mDefaultSession =
            ContentRecordingSession.createDisplaySession(TEST_TOKEN);
    private static Point sSurfaceSize;
    private ContentRecorder mContentRecorder;
    private SurfaceControl mRecordedSurface;

    @Before public void setUp() {
        // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
        // mirror.
        setUpDefaultTaskDisplayAreaWindowToken();

        // GIVEN SurfaceControl can successfully mirror the provided surface.
        sSurfaceSize = new Point(
                mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
                mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
        mRecordedSurface = surfaceControlMirrors(sSurfaceSize);

        // GIVEN the VirtualDisplay associated with the session (so the display has state ON).
        VirtualDisplay virtualDisplay = mWm.mDisplayManager.createVirtualDisplay("VirtualDisplay",
                sSurfaceSize.x, sSurfaceSize.y,
                DisplayMetrics.DENSITY_140, new Surface(), VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR);
        final int displayId = virtualDisplay.getDisplay().getDisplayId();
        mDefaultSession.setDisplayId(displayId);

        mWm.mRoot.onDisplayAdded(displayId);
        final DisplayContent mVirtualDisplayContent = mWm.mRoot.getDisplayContent(displayId);
        mContentRecorder = new ContentRecorder(mVirtualDisplayContent);
        spyOn(mVirtualDisplayContent);
    }

    @Test
    public void testIsCurrentlyRecording() {
        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();

        mContentRecorder.updateRecording();
        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
    }

    @Test
    public void testUpdateRecording_display() {
        mContentRecorder.setContentRecordingSession(mDefaultSession);
        mContentRecorder.updateRecording();
        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
    }

    @Test
    public void testUpdateRecording_task() {
        mDefaultSession.setContentToRecord(RECORD_CONTENT_TASK);
        mContentRecorder.setContentRecordingSession(mDefaultSession);
        mContentRecorder.updateRecording();
        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
    }

    @Test
    public void testUpdateRecording_wasPaused() {
        mContentRecorder.setContentRecordingSession(mDefaultSession);
        mContentRecorder.updateRecording();

        mContentRecorder.pauseRecording();
        mContentRecorder.updateRecording();
        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
    }

    @Test
    public void testUpdateRecording_wasStopped() {
        mContentRecorder.setContentRecordingSession(mDefaultSession);
        mContentRecorder.updateRecording();

        mContentRecorder.remove();
        mContentRecorder.updateRecording();
        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
    }

    @Test
    public void testOnConfigurationChanged_neverRecording() {
        mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT);

        verify(mTransaction, never()).setPosition(eq(mRecordedSurface), anyFloat(), anyFloat());
        verify(mTransaction, never()).setMatrix(eq(mRecordedSurface), anyFloat(), anyFloat(),
                anyFloat(), anyFloat());
    }

    @Test
    public void testOnConfigurationChanged_resizesSurface() {
        mContentRecorder.setContentRecordingSession(mDefaultSession);
        mContentRecorder.updateRecording();
        mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT);

        verify(mTransaction, atLeastOnce()).setPosition(eq(mRecordedSurface), anyFloat(),
                anyFloat());
        verify(mTransaction, atLeastOnce()).setMatrix(eq(mRecordedSurface), anyFloat(), anyFloat(),
                anyFloat(), anyFloat());
    }

    @Test
    public void testPauseRecording_pausesRecording() {
        mContentRecorder.setContentRecordingSession(mDefaultSession);
        mContentRecorder.updateRecording();

        mContentRecorder.pauseRecording();
        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
    }

    @Test
    public void testPauseRecording_neverRecording() {
        mContentRecorder.pauseRecording();
        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
    }

    @Test
    public void testRemove_stopsRecording() {
        mContentRecorder.setContentRecordingSession(mDefaultSession);
        mContentRecorder.updateRecording();

        mContentRecorder.remove();
        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
    }

    @Test
    public void testRemove_neverRecording() {
        mContentRecorder.remove();
        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
    }

    @Test
    public void testUpdateMirroredSurface_capturedAreaResized() {
        mContentRecorder.setContentRecordingSession(mDefaultSession);
        mContentRecorder.updateRecording();

        // WHEN attempting to mirror on the virtual display, and the captured content is resized.
        float xScale = 0.7f;
        float yScale = 2f;
        Rect displayAreaBounds = new Rect(0, 0, Math.round(sSurfaceSize.x * xScale),
                Math.round(sSurfaceSize.y * yScale));
        mContentRecorder.updateMirroredSurface(mTransaction, displayAreaBounds, sSurfaceSize);

        // THEN content in the captured DisplayArea is scaled to fit the surface size.
        verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, 1.0f / yScale, 0, 0,
                1.0f / yScale);
        // THEN captured content is positioned in the centre of the output surface.
        float scaledWidth = displayAreaBounds.width() / xScale;
        float xInset = (sSurfaceSize.x - scaledWidth) / 2;
        verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface, xInset, 0);
    }

    private static class RecordingTestToken extends Binder {
    }

    /**
     * Creates a WindowToken associated with the default task DisplayArea, in order for that
     * DisplayArea to be mirrored.
     */
    private void setUpDefaultTaskDisplayAreaWindowToken() {
        // GIVEN the default task display area is represented by the WindowToken.
        spyOn(mWm.mWindowContextListenerController);
        doReturn(mDefaultDisplay.getDefaultTaskDisplayArea()).when(
                mWm.mWindowContextListenerController).getContainer(any());
    }

    /**
     * SurfaceControl successfully creates a mirrored surface of the given size.
     */
    private SurfaceControl surfaceControlMirrors(Point surfaceSize) {
        // Do not set the parent, since the mirrored surface is the root of a new surface hierarchy.
        SurfaceControl mirroredSurface = new SurfaceControl.Builder()
                .setName("mirroredSurface")
                .setBufferSize(surfaceSize.x, surfaceSize.y)
                .setCallsite("mirrorSurface")
                .build();
        doReturn(mirroredSurface).when(() -> SurfaceControl.mirrorSurface(any()));
        doReturn(surfaceSize).when(mWm.mDisplayManagerInternal).getDisplaySurfaceDefaultSize(
                anyInt());
        return mirroredSurface;
    }
}
+1 −106

File changed.

Preview size limit exceeded, changes collapsed.

Loading