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

Commit f103383f authored by Ikram Gabiyev's avatar Ikram Gabiyev Committed by Android (Google) Code Review
Browse files

Merge "System Perf Hints in PiP" into main

parents 769510be 6bb0ca6f
Loading
Loading
Loading
Loading
+166 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.wm.shell.common.pip;

import static android.window.SystemPerformanceHinter.HINT_SF;

import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE;

import android.window.SystemPerformanceHinter;
import android.window.SystemPerformanceHinter.HighPerfSession;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ShellMainThread;

import java.io.PrintWriter;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.function.Consumer;

/**
 * Manages system performance hints for PiP CUJs and interactions.
 */
public class PipPerfHintController {
    private static final String TAG = PipPerfHintController.class.getSimpleName();

    // Delay until signal about a session cleanup is sent.
    private static final int SESSION_TIMEOUT_DELAY = 20_000;

    // Maximum number of possible high perf session.
    private static final int SESSION_POOL_SIZE = 20;

    private final SystemPerformanceHinter mSystemPerformanceHinter;
    @NonNull
    private final PipDisplayLayoutState mPipDisplayLayoutState;
    @NonNull
    private final ShellExecutor mMainExecutor;


    public PipPerfHintController(@NonNull PipDisplayLayoutState pipDisplayLayoutState,
            @ShellMainThread ShellExecutor mainExecutor,
            @NonNull SystemPerformanceHinter systemPerformanceHinter) {
        mPipDisplayLayoutState = pipDisplayLayoutState;
        mMainExecutor = mainExecutor;
        mSystemPerformanceHinter = systemPerformanceHinter;
    }

    /**
     * Starts a high perf session.
     *
     * @param timeoutCallback an optional callback to be executed upon session timeout.
     * @return a wrapper around the session to allow for early closing; null if no free sessions
     * left available in the pool.
     */
    @Nullable
    public PipHighPerfSession startSession(@Nullable Consumer<PipHighPerfSession> timeoutCallback,
            String reason) {
        if (PipHighPerfSession.getActiveSessionsCount() == SESSION_POOL_SIZE) {
            return null;
        }

        HighPerfSession highPerfSession = mSystemPerformanceHinter.startSession(HINT_SF,
                mPipDisplayLayoutState.getDisplayId(), "pip-high-perf-session");
        PipHighPerfSession pipHighPerfSession = new PipHighPerfSession(highPerfSession, reason);

        if (timeoutCallback != null) {
            mMainExecutor.executeDelayed(() -> {
                if (PipHighPerfSession.hasClosedOrFinalized(pipHighPerfSession)) {
                    // If the session is either directly closed or GC collected before timeout
                    // was reached, do not send the timeout callback.
                    return;
                }
                // The session hasn't been closed yet, so do that now, along with any cleanup.
                pipHighPerfSession.close();
                ProtoLog.d(WM_SHELL_PICTURE_IN_PICTURE, "%s: high perf session %s timed out", TAG,
                        pipHighPerfSession.toString());
                timeoutCallback.accept(pipHighPerfSession);
            }, SESSION_TIMEOUT_DELAY);
        }
        ProtoLog.d(WM_SHELL_PICTURE_IN_PICTURE, "%s: high perf session %s is started",
                TAG, pipHighPerfSession.toString());
        return pipHighPerfSession;
    }

    /**
     * Dumps the inner state.
     */
    public void dump(PrintWriter pw, String prefix) {
        final String innerPrefix = prefix + "  ";
        pw.println(prefix + TAG);
        pw.println(innerPrefix + "activeSessionCount="
                + PipHighPerfSession.getActiveSessionsCount());
    }

    /**
     * A wrapper around {@link HighPerfSession} to keep track of some extra metadata about
     * the session's status.
     */
    public class PipHighPerfSession implements AutoCloseable{

        // THe actual HighPerfSession we wrap around.
        private final HighPerfSession mSession;

        private final String mReason;

        /**
         * Keeps track of all active sessions using weakly referenced keys.
         * This makes sure that that sessions do not get accidentally leaked if not closed.
         */
        private static Map<PipHighPerfSession, Boolean> sActiveSessions = new WeakHashMap<>();

        private PipHighPerfSession(HighPerfSession session, String reason) {
            mSession = session;
            mReason = reason;
            sActiveSessions.put(this, true);
        }

        /**
         * Closes a high perf session.
         */
        @Override
        public void close() {
            sActiveSessions.remove(this);
            mSession.close();
            ProtoLog.d(WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: high perf session %s is closed",
                    TAG, toString());
        }

        @Override
        public void finalize() {
            // The entry should be removed from the weak hash map as well by default.
            mSession.close();
        }

        @Override
        public String toString() {
            return "[" + super.toString() + "] initially started due to: " + mReason;
        }

        private static boolean hasClosedOrFinalized(PipHighPerfSession pipHighPerfSession) {
            return !sActiveSessions.containsKey(pipHighPerfSession);
        }

        private static int getActiveSessionsCount() {
            return sActiveSessions.size();
        }
    }
}
+15 −0
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
@@ -394,6 +395,20 @@ public abstract class WMShellBaseModule {
        return new PhoneSizeSpecSource(context, pipDisplayLayoutState);
    }

    @WMSingleton
    @Provides
    static Optional<PipPerfHintController> providePipPerfHintController(
            PipDisplayLayoutState pipDisplayLayoutState,
            @ShellMainThread ShellExecutor mainExecutor,
            Optional<SystemPerformanceHinter> systemPerformanceHinterOptional) {
        if (systemPerformanceHinterOptional.isPresent()) {
            return Optional.of(new PipPerfHintController(pipDisplayLayoutState, mainExecutor,
                    systemPerformanceHinterOptional.get()));
        } else {
            return Optional.empty();
        }
    }

    @WMSingleton
    @Provides
    static PipBoundsState providePipBoundsState(Context context,
+11 −6
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
@@ -143,10 +144,12 @@ public abstract class Pip1Module {
            PipMotionHelper pipMotionHelper,
            FloatingContentCoordinator floatingContentCoordinator,
            PipUiEventLogger pipUiEventLogger,
            @ShellMainThread ShellExecutor mainExecutor) {
            @ShellMainThread ShellExecutor mainExecutor,
            Optional<PipPerfHintController> pipPerfHintControllerOptional) {
        return new PipTouchHandler(context, shellInit, menuPhoneController, pipBoundsAlgorithm,
                pipBoundsState, sizeSpecSource, pipTaskOrganizer, pipMotionHelper,
                floatingContentCoordinator, pipUiEventLogger, mainExecutor);
                floatingContentCoordinator, pipUiEventLogger, mainExecutor,
                pipPerfHintControllerOptional);
    }

    @WMSingleton
@@ -169,6 +172,7 @@ public abstract class Pip1Module {
            PipTransitionController pipTransitionController,
            PipParamsChangedForwarder pipParamsChangedForwarder,
            Optional<SplitScreenController> splitScreenControllerOptional,
            Optional<PipPerfHintController> pipPerfHintControllerOptional,
            DisplayController displayController,
            PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
            @ShellMainThread ShellExecutor mainExecutor) {
@@ -176,8 +180,8 @@ public abstract class Pip1Module {
                syncTransactionQueue, pipTransitionState, pipBoundsState, pipDisplayLayoutState,
                pipBoundsAlgorithm, menuPhoneController, pipAnimationController,
                pipSurfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
                splitScreenControllerOptional, displayController, pipUiEventLogger,
                shellTaskOrganizer, mainExecutor);
                splitScreenControllerOptional, pipPerfHintControllerOptional, displayController,
                pipUiEventLogger, shellTaskOrganizer, mainExecutor);
    }

    @WMSingleton
@@ -209,10 +213,11 @@ public abstract class Pip1Module {
            PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer,
            PhonePipMenuController menuController, PipSnapAlgorithm pipSnapAlgorithm,
            PipTransitionController pipTransitionController,
            FloatingContentCoordinator floatingContentCoordinator) {
            FloatingContentCoordinator floatingContentCoordinator,
            Optional<PipPerfHintController> pipPerfHintControllerOptional) {
        return new PipMotionHelper(context, pipBoundsState, pipTaskOrganizer,
                menuController, pipSnapAlgorithm, pipTransitionController,
                floatingContentCoordinator);
                floatingContentCoordinator, pipPerfHintControllerOptional);
    }

    @WMSingleton
+4 −2
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import com.android.wm.shell.common.pip.LegacySizeSpecSource;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.dagger.WMShellBaseModule;
@@ -212,6 +213,7 @@ public abstract class TvPipModule {
            PipParamsChangedForwarder pipParamsChangedForwarder,
            PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
            Optional<SplitScreenController> splitScreenControllerOptional,
            Optional<PipPerfHintController> pipPerfHintControllerOptional,
            DisplayController displayController,
            PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
            @ShellMainThread ShellExecutor mainExecutor) {
@@ -219,8 +221,8 @@ public abstract class TvPipModule {
                syncTransactionQueue, pipTransitionState, tvPipBoundsState, pipDisplayLayoutState,
                tvPipBoundsAlgorithm, tvPipMenuController, pipAnimationController,
                pipSurfaceTransactionHelper, tvPipTransition, pipParamsChangedForwarder,
                splitScreenControllerOptional, displayController, pipUiEventLogger,
                shellTaskOrganizer, mainExecutor);
                splitScreenControllerOptional, pipPerfHintControllerOptional, displayController,
                pipUiEventLogger, shellTaskOrganizer, mainExecutor);
    }

    @WMSingleton
+30 −0
Original line number Diff line number Diff line
@@ -86,6 +86,7 @@ import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMenuController;
import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.phone.PipMotionHelper;
@@ -140,6 +141,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
    private final int mCrossFadeAnimationDuration;
    private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
    private final Optional<SplitScreenController> mSplitScreenOptional;
    @Nullable private final PipPerfHintController mPipPerfHintController;
    protected final ShellTaskOrganizer mTaskOrganizer;
    protected final ShellExecutor mMainExecutor;

@@ -157,10 +159,30 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
    private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
            new PipAnimationController.PipAnimationCallback() {
                private boolean mIsCancelled;
                @Nullable private PipPerfHintController.PipHighPerfSession mPipHighPerfSession;

                private void onHighPerfSessionTimeout(
                        PipPerfHintController.PipHighPerfSession session) {}

                private void cleanUpHighPerfSessionMaybe() {
                    if (mPipHighPerfSession != null) {
                        // Close the high perf session once pointer interactions are over;
                        mPipHighPerfSession.close();
                        mPipHighPerfSession = null;
                    }
                }


                @Override
                public void onPipAnimationStart(TaskInfo taskInfo,
                        PipAnimationController.PipTransitionAnimator animator) {
                    if (mPipPerfHintController != null) {
                        // Start a high perf session with a timeout callback.
                        mPipHighPerfSession = mPipPerfHintController.startSession(
                                this::onHighPerfSessionTimeout,
                                "PipTaskOrganizer::mPipAnimationCallback");
                    }

                    final int direction = animator.getTransitionDirection();
                    mIsCancelled = false;
                    sendOnPipTransitionStarted(direction);
@@ -169,6 +191,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
                @Override
                public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx,
                        PipAnimationController.PipTransitionAnimator animator) {
                    // Close the high perf session if needed.
                    cleanUpHighPerfSessionMaybe();

                    final int direction = animator.getTransitionDirection();
                    if (mIsCancelled) {
                        sendOnPipTransitionFinished(direction);
@@ -356,6 +381,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            @NonNull PipTransitionController pipTransitionController,
            @NonNull PipParamsChangedForwarder pipParamsChangedForwarder,
            Optional<SplitScreenController> splitScreenOptional,
            Optional<PipPerfHintController> pipPerfHintControllerOptional,
            @NonNull DisplayController displayController,
            @NonNull PipUiEventLogger pipUiEventLogger,
            @NonNull ShellTaskOrganizer shellTaskOrganizer,
@@ -381,6 +407,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        mSurfaceControlTransactionFactory =
                new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
        mSplitScreenOptional = splitScreenOptional;
        mPipPerfHintController = pipPerfHintControllerOptional.orElse(null);
        mTaskOrganizer = shellTaskOrganizer;
        mMainExecutor = mainExecutor;

@@ -1972,6 +1999,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState());
        pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
        mPipTransitionController.dump(pw, innerPrefix);
        if (mPipPerfHintController != null) {
            mPipPerfHintController.dump(pw, innerPrefix);
        }
    }

    @Override
Loading