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

Commit 56fb3d89 authored by Marzia Favaro's avatar Marzia Favaro
Browse files

Add interface to store compatibility between handlers and transitions

Bug: 402454136
Test: N/A code not yet executed
Flag: com.android.window.flags.enable_handlers_debugging_mode
Change-Id: I5288c986fd4f38637b39aba829b0177243d406c1
parent bca73325
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -539,6 +539,13 @@ flag {
    }
}

flag {
    name: "enable_handlers_debugging_mode"
    namespace: "windowing_frontend"
    description: "collect data on handlers that did or did not play"
    bug: "402454136"
}

flag {
    name: "enable_multidisplay_trackpad_back_gesture"
    namespace: "lse_desktop_experience"
+254 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.transition;

import static com.android.wm.shell.transition.Transitions.transitTypeToString;

import android.annotation.NonNull;
import android.os.IBinder;
import android.util.ArrayMap;
import android.view.WindowManager;
import android.window.TransitionInfo;

import androidx.annotation.IntDef;

import java.util.ArrayList;

public class TransitionDispatchState {
    public static final int NONE = 0;
    // Flag-related errors
    public static final int LOST_RELEVANT_FLAG = 1;
    public static final int CAPTURED_UNRELATED_FLAG = 2;
    // Change-related errors
    public static final int LOST_RELEVANT_CHANGE = 3;
    public static final int CAPTURED_UNRELATED_CHANGE = 4;

    @IntDef(
            value = {
                NONE,
                LOST_RELEVANT_FLAG,
                CAPTURED_UNRELATED_FLAG,
                LOST_RELEVANT_CHANGE,
                CAPTURED_UNRELATED_CHANGE,
            })
    public @interface ErrorCode {}

    static String toString(@ErrorCode int errorCode) {
        return switch (errorCode) {
            case NONE -> "NONE";
            case LOST_RELEVANT_FLAG -> "LOST_RELEVANT_FLAG";
            case CAPTURED_UNRELATED_FLAG -> "CAPTURED_UNRELATED_FLAG";
            case LOST_RELEVANT_CHANGE -> "LOST_RELEVANT_CHANGE";
            case CAPTURED_UNRELATED_CHANGE -> "CAPTURED_UNRELATED_CHANGE";
            default -> "UNKNOWN";
        };
    }

    final ArrayMap<Transitions.TransitionHandler, HandlerData>
            mHandlersData = new ArrayMap<>();
    final IBinder mTransition;
    public final TransitionInfo mInfo;

    private static DummyTransitionDispatchState sDummyInstance;

    private TransitionDispatchState() {
        // Can only be used only by dummy instance.
        mTransition = null;
        mInfo = null;
    }

    public TransitionDispatchState(@NonNull IBinder transition, @NonNull TransitionInfo info) {
        mTransition = transition;
        mInfo = info;
    }

    /**
     * @return a dummy instance of {@link TransitionDispatchState} that does nothing.
     */
    public static TransitionDispatchState getDummyInstance() {
        if (sDummyInstance == null) {
            sDummyInstance = new DummyTransitionDispatchState();
        }
        return sDummyInstance;
    }

    /**
     * @param handler the handler that is interested in this transition.
     * @param change  If the handler has consumed the transition, this is a change that has not been
     *                animated.
     *                If the handler has not consumed it, this is a change that the handler would
     *                have been interested in animating.
     * @param errorCode an error code to identify the error type.
     */
    public void addError(@NonNull Transitions.TransitionHandler handler,
                         @NonNull TransitionInfo.Change change, @ErrorCode int errorCode) {
        HandlerData handlerData = getDataOrCreate(handler);
        HandlerData.Error error =
                new HandlerData.Error(change, errorCode);
        handlerData.mErrors.add(error);
    }

    /**
     * @param handler the handler that is interested in this transition.
     * @param flag    a flag that the handler finds relevant, either because it should have been
     *                handled by handler (and it didn't) or because it was not relevant for handling
     *                the transition (which was played by handler).
     * @param errorCode an error code to identify the error type.
     */
    public void addError(@NonNull Transitions.TransitionHandler handler,
                         @WindowManager.TransitionFlags int flag, @ErrorCode int errorCode) {
        HandlerData handlerData = getDataOrCreate(handler);
        HandlerData.Error error =
                new HandlerData.Error(flag, errorCode);
        handlerData.mErrors.add(error);
    }

    /**
     * Sets that {@param handler} has played at least part of this transition.
     */
    public void setHasPlayed(@NonNull Transitions.TransitionHandler handler) {
        HandlerData handlerData = getDataOrCreate(handler);
        handlerData.mHasPlayed = true;
    }

    /**
     * @return true if {@param handler} has played at least part of this transition.
     */
    public boolean hasPlayed(@NonNull Transitions.TransitionHandler handler) {
        HandlerData handlerData = mHandlersData.get(handler);
        if (handlerData == null) {
            return false;
        }
        return handlerData.mHasPlayed;
    }

    /**
     * @return true if {@param handler} has found problems with the handling of this transition.
     */
    public boolean hasErrors(@NonNull Transitions.TransitionHandler handler) {
        HandlerData handlerData = mHandlersData.get(handler);
        if (handlerData == null) {
            return false;
        }
        return !handlerData.mErrors.isEmpty();
    }

    /**
     * Report all the errors found by the handlers to an atom.
     */
    public void reportWarnings() {
        throw new UnsupportedOperationException("Not implemented");
    }

    /**
     * @return a string containing the analyzed handlers and the information they collected.
     */
    public String getDebugInfo() {
        StringBuilder sb = new StringBuilder();
        sb.append("Transition #").append(mInfo.getDebugId()).append(": {");
        for (int i = 0; i < mHandlersData.size(); i++) {
            HandlerData handlerData = mHandlersData.valueAt(i);
            if (!handlerData.mErrors.isEmpty() || handlerData.mHasPlayed) {
                // Handler + collected info
                sb.append("\n    ");
                sb.append(handlerData.mHandler.getClass().getSimpleName()).append(": [");
                sb.append("hasPlayed=").append(handlerData.mHasPlayed);
                for (int j = 0; j < handlerData.mErrors.size(); j++) {
                    HandlerData.Error err = handlerData.mErrors.get(j);
                    sb.append(" Errors={");
                    if (err.mChange != null) {
                        sb.append("ChangeError(").append(toString(err.mErrorCode));
                        sb.append(" -- ").append(err.mChange);
                        sb.append("), ");
                    } else if (err.mTransitFlag != 0) {
                        sb.append("FlagError(").append(toString(err.mErrorCode));
                        sb.append(" -- ").append(transitTypeToString(err.mTransitFlag));
                        sb.append("}, ");
                    }
                }
                sb.append("]");
            }
        }
        sb.append("\n}");
        return sb.toString();
    }

    private HandlerData getDataOrCreate(
            @NonNull Transitions.TransitionHandler handler) {
        HandlerData handlerData = mHandlersData.get(handler);
        if (handlerData != null) return handlerData;
        handlerData = new HandlerData(handler);
        mHandlersData.put(handler, handlerData);
        return handlerData;
    }


    /**
     * HandlerData collects errors and information relative to a handler
     * in relation to a transition.
     */
    static class HandlerData {
        /**
         * Collects information about errors found by a handler
         */
        static class Error {
            final TransitionInfo.Change mChange;
            final @WindowManager.TransitionFlags int mTransitFlag;
            final @ErrorCode int mErrorCode;

            Error(int flag, @ErrorCode int errorCode) {
                mTransitFlag = flag;
                mErrorCode = errorCode;
                mChange = null;
            }

            Error(@NonNull TransitionInfo.Change change, @ErrorCode int errorCode) {
                mChange = change;
                mErrorCode = errorCode;
                mTransitFlag = 0;
            }
        }

        final Transitions.TransitionHandler mHandler;
        final ArrayList<Error> mErrors = new ArrayList<>();
        boolean mHasPlayed = false;

        HandlerData(Transitions.TransitionHandler handler) {
            mHandler = handler;
        }
    }

    /**
     * Dummy implementation of {@link TransitionDispatchState} that does nothing.
     * This is used temporary until Transitions.TransitionHandler.startAnimation(...) without
     * TransitionDispatchState is deprecated.
     */
    private static class DummyTransitionDispatchState extends TransitionDispatchState {
        @Override
        public void addError(@NonNull Transitions.TransitionHandler handler,
                             @NonNull TransitionInfo.Change change,
                             @ErrorCode int errorCode) {}

        @Override
        public void addError(@NonNull Transitions.TransitionHandler handler,
                             @WindowManager.TransitionFlags int flag,
                             @ErrorCode int errorCode) {}

        @Override
        public void setHasPlayed(@NonNull Transitions.TransitionHandler handler) {}
    }
}
+20 −0
Original line number Diff line number Diff line
@@ -1435,6 +1435,26 @@ public class Transitions implements RemoteCallable<Transitions>,
                @NonNull SurfaceControl.Transaction finishTransaction,
                @NonNull TransitionFinishCallback finishCallback);

        /**
         * Like {@link #startAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction,
         * SurfaceControl.Transaction, TransitionFinishCallback)} when {@param info} is not null.
         * When {@param info} is null, startAnimation won't do any active animation, but will just
         * collect information about the compatibility of the handler and the transition in
         * {@param dispatchState}.
         */
        default boolean startAnimation(@NonNull IBinder transition,
                                       @Nullable TransitionInfo consumableInfo,
                                       @NonNull TransitionDispatchState dispatchState,
                                       @NonNull SurfaceControl.Transaction startTransaction,
                                       @NonNull SurfaceControl.Transaction finishTransaction,
                                       @NonNull TransitionFinishCallback finishCallback) {
            if (consumableInfo != null) {
                return startAnimation(transition, consumableInfo, startTransaction,
                        finishTransaction, finishCallback);
            }
            return false;
        }

        /**
         * See {@link #mergeAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction, SurfaceControl.Transaction, IBinder, TransitionFinishCallback)}
         *