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

Commit f29c06a9 authored by Chavi Weingarten's avatar Chavi Weingarten
Browse files

Add SurfaceControlInputReceiver

Allow SurfaceControls to be registered to receive input without
needing a window.

Test: SurfaceControlInputReceiverTests
Bug: 278757236
Change-Id: Icb9c7cd518bddfa3b5569d59b79610fd7505a2e4
parent 469ee63b
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -51847,6 +51847,10 @@ package android.view {
    ctor public SurfaceControl.TrustedPresentationThresholds(@FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @IntRange(from=1) int);
  }
  @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public interface SurfaceControlInputReceiver {
    method public boolean onInputEvent(@NonNull android.view.InputEvent);
  }
  public class SurfaceControlViewHost {
    ctor public SurfaceControlViewHost(@NonNull android.content.Context, @NonNull android.view.Display, @Nullable android.os.IBinder);
    method @Nullable public android.view.SurfaceControlViewHost.SurfacePackage getSurfacePackage();
@@ -53970,10 +53974,13 @@ package android.view {
    method @Deprecated public android.view.Display getDefaultDisplay();
    method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics();
    method public default boolean isCrossWindowBlurEnabled();
    method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.os.IBinder registerBatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.view.Choreographer, @NonNull android.view.SurfaceControlInputReceiver);
    method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void registerTrustedPresentationListener(@NonNull android.os.IBinder, @NonNull android.window.TrustedPresentationThresholds, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
    method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.os.IBinder registerUnbatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.os.Looper, @NonNull android.view.SurfaceControlInputReceiver);
    method public default void removeCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
    method public default void removeProposedRotationListener(@NonNull java.util.function.IntConsumer);
    method public void removeViewImmediate(android.view.View);
    method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void unregisterSurfaceControlInputReceiver(@NonNull android.os.IBinder);
    method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void unregisterTrustedPresentationListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
    field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
    field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
+7 −0
Original line number Diff line number Diff line
@@ -377,6 +377,13 @@ public final class Choreographer {
        return mLooper == looper;
    }

    /**
     * @hide
     */
    public Looper getLooper() {
        return mLooper;
    }

    /**
     * The amount of time, in milliseconds, between each frame of the animation.
     * <p>
+43 −0
Original line number Diff line number Diff line
/*
 * Copyright 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 android.view;

import android.annotation.FlaggedApi;
import android.annotation.NonNull;

import com.android.window.flags.Flags;

/**
 * Provides a mechanism for a SurfaceControl to receive input events.
 */
@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
public interface SurfaceControlInputReceiver {
    /**
     * When input events are batched, this is called at most once per frame. When non batched, this
     * is called immediately for the input event.
     *
     * @param event The input event that was received. This input event object will become invalid
     *              and recycled after this method is invoked. If there is need to persist this
     *              object beyond the scope of this method, the overriding code should make a copy
     *              of this object. For example, using
     *              {@link MotionEvent#obtain(MotionEvent other)} or
     *              {@link KeyEvent#KeyEvent(KeyEvent)} }
     * @return true if the event was handled, false otherwise.
     */
    boolean onInputEvent(@NonNull InputEvent event);

}
+91 −0
Original line number Diff line number Diff line
@@ -109,6 +109,7 @@ import android.graphics.Region;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemProperties;
@@ -6015,4 +6016,94 @@ public interface WindowManager extends ViewManager {
    default void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) {
        throw new UnsupportedOperationException();
    }

    /**
     * Registers a {@link SurfaceControlInputReceiver} for a {@link SurfaceControl} that will
     * receive batched input event. For those events that are batched, the invocation will happen
     * once per {@link Choreographer} frame, and other input events will be delivered immediately.
     * This is different from
     * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper,
     * SurfaceControlInputReceiver)} in that the input events are received batched. The caller must
     * invoke {@link #unregisterSurfaceControlInputReceiver(IBinder)} to clean up the resources when
     * no longer needing to use the {@link SurfaceControlInputReceiver}
     *
     * @param displayId      The display that the SurfaceControl will be placed on. Input will
     *                       only work
     *                       if SurfaceControl is on that display and that display was touched.
     * @param surfaceControl The SurfaceControl to register the InputChannel for
     * @param hostToken      The host token to link the InputChannel for. This is primarily for ANRs
     *                       to ensure the host receives the ANR if any issues with touch on the
     *                       InputChannel
     * @param choreographer  The Choreographer used for batching. This should match the rendering
     *                       Choreographer.
     * @param receiver       The SurfaceControlInputReceiver that will receive the input events
     * @return an {@link IBinder} token that is used to unregister the input receiver via
     * {@link #unregisterSurfaceControlInputReceiver(IBinder)}.
     * @see #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper,
     * SurfaceControlInputReceiver)
     */
    @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
    @NonNull
    default IBinder registerBatchedSurfaceControlInputReceiver(int displayId,
            @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl,
            @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) {
        throw new UnsupportedOperationException(
                "registerBatchedSurfaceControlInputReceiver is not implemented");
    }

    /**
     * Registers a {@link SurfaceControlInputReceiver} for a {@link SurfaceControl} that will
     * receive every input event. This is different than calling @link
     * #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer,
     * SurfaceControlInputReceiver)} in that the input events are received unbatched. The caller
     * must invoke {@link #unregisterSurfaceControlInputReceiver(IBinder)} to clean up the resources
     * when no longer needing to use the {@link SurfaceControlInputReceiver}
     *
     * @param displayId      The display that the SurfaceControl will be placed on. Input will only
     *                       work if SurfaceControl is on that display and that display was
     *                       touched.
     * @param hostToken      The host token to link the InputChannel for. This is primarily for ANRs
     *                       to ensure the host receives the ANR if any issues with touch on the
     *                       InputChannel
     * @param surfaceControl The SurfaceControl to register the InputChannel for
     * @param looper         The looper to use when invoking callbacks.
     * @param receiver       The SurfaceControlInputReceiver that will receive the input events
     * @return an {@link IBinder} token that is used to unregister the input receiver via
     * {@link #unregisterSurfaceControlInputReceiver(IBinder)}.
     * @see #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer,
     * SurfaceControlInputReceiver)
     **/
    @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
    @NonNull
    default IBinder registerUnbatchedSurfaceControlInputReceiver(int displayId,
            @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl,
            @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) {
        throw new UnsupportedOperationException(
                "registerUnbatchedSurfaceControlInputReceiver is not implemented");
    }

    /**
     * Unregisters and cleans up the registered {@link SurfaceControlInputReceiver} for the
     * specified token.
     * <p>
     * Must be called on the same {@link Looper} thread to which was passed to the
     * {@link #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl,
     * Choreographer,
     * SurfaceControlInputReceiver)} or
     * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper,
     * SurfaceControlInputReceiver)}
     *
     * @param token The token that was returned via
     *              {@link #registerBatchedSurfaceControlInputReceiver(int, IBinder,
     *              SurfaceControl,
     *              Choreographer, SurfaceControlInputReceiver)} or
     *              {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder,
     *              SurfaceControl,
     *              Looper, SurfaceControlInputReceiver)}
     */
    @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
    default void unregisterSurfaceControlInputReceiver(@NonNull IBinder token) {
        throw new UnsupportedOperationException(
                "unregisterSurfaceControlInputReceiver is not implemented");
    }
}
+76 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.view;

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

import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -24,8 +26,10 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.graphics.HardwareRenderer;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -46,6 +50,7 @@ import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
@@ -151,6 +156,9 @@ public final class WindowManagerGlobal {
    private final TrustedPresentationListener mTrustedPresentationListener =
            new TrustedPresentationListener();

    private final ConcurrentHashMap<IBinder, InputEventReceiver> mSurfaceControlInputReceivers =
            new ConcurrentHashMap<>();

    private WindowManagerGlobal() {
    }

@@ -808,6 +816,74 @@ public final class WindowManagerGlobal {
        mTrustedPresentationListener.removeListener(listener);
    }

    IBinder registerBatchedSurfaceControlInputReceiver(int displayId,
            @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl,
            @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) {
        IBinder clientToken = new Binder();
        InputChannel inputChannel = new InputChannel();
        try {
            WindowManagerGlobal.getWindowSession().grantInputChannel(displayId, surfaceControl,
                    clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, null,
                    surfaceControl.getName(), inputChannel);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to create input channel", e);
            e.rethrowAsRuntimeException();
        }

        mSurfaceControlInputReceivers.put(clientToken,
                new BatchedInputEventReceiver(inputChannel, choreographer.getLooper(),
                        choreographer) {
                    @Override
                    public void onInputEvent(InputEvent event) {
                        boolean handled = receiver.onInputEvent(event);
                        finishInputEvent(event, handled);
                    }
                });
        return clientToken;
    }

    IBinder registerUnbatchedSurfaceControlInputReceiver(
            int displayId, @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl,
            @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) {
        IBinder clientToken = new Binder();
        InputChannel inputChannel = new InputChannel();
        try {
            WindowManagerGlobal.getWindowSession().grantInputChannel(displayId, surfaceControl,
                    clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, null,
                    surfaceControl.getName(), inputChannel);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to create input channel", e);
            e.rethrowAsRuntimeException();
        }

        mSurfaceControlInputReceivers.put(clientToken,
                new InputEventReceiver(inputChannel, looper) {
                    @Override
                    public void onInputEvent(InputEvent event) {
                        boolean handled = receiver.onInputEvent(event);
                        finishInputEvent(event, handled);
                    }
                });

        return clientToken;
    }

    void unregisterSurfaceControlInputReceiver(IBinder token) {
        InputEventReceiver inputEventReceiver = mSurfaceControlInputReceivers.get(token);
        if (inputEventReceiver == null) {
            Log.w(TAG, "No registered input event receiver with token: " + token);
            return;
        }
        try {
            WindowManagerGlobal.getWindowSession().remove(token);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to remove input channel", e);
            e.rethrowAsRuntimeException();
        }

        inputEventReceiver.dispose();
    }

    private final class TrustedPresentationListener extends
            ITrustedPresentationListener.Stub {
        private static int sId = 0;
Loading