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

Commit f810101b authored by Patrick Williams's avatar Patrick Williams
Browse files

Add WindowInfosListenerForTest

Adds wrapper class WindowInfosListenerForTest that enables using
WindowInfosListener in tests.

Bug: 263311858
Test: presubmits
Change-Id: I8a5f83b0557db26aa0d05e1ffdfcbb7d374afc38
parent 26043b29
Loading
Loading
Loading
Loading
+12 −0
Original line number Original line Diff line number Diff line
@@ -3923,6 +3923,18 @@ package android.window {
    method public abstract void onTransactionReady(int, @NonNull android.view.SurfaceControl.Transaction);
    method public abstract void onTransactionReady(int, @NonNull android.view.SurfaceControl.Transaction);
  }
  }


  public class WindowInfosListenerForTest {
    ctor public WindowInfosListenerForTest();
    method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public void addWindowInfosListener(@NonNull java.util.function.Consumer<java.util.List<android.window.WindowInfosListenerForTest.WindowInfo>>);
    method public void removeWindowInfosListener(@NonNull java.util.function.Consumer<java.util.List<android.window.WindowInfosListenerForTest.WindowInfo>>);
  }

  public static class WindowInfosListenerForTest.WindowInfo {
    field @NonNull public final android.graphics.Rect bounds;
    field @NonNull public final String name;
    field @NonNull public final android.os.IBinder windowToken;
  }

  public class WindowOrganizer {
  public class WindowOrganizer {
    ctor public WindowOrganizer();
    ctor public WindowOrganizer();
    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public int applySyncTransaction(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.WindowContainerTransactionCallback);
    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public int applySyncTransaction(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.WindowContainerTransactionCallback);
+4 −0
Original line number Original line Diff line number Diff line
@@ -244,6 +244,10 @@ public final class InputWindowHandle {
        window = iwindow;
        window = iwindow;
    }
    }


    public @Nullable IBinder getWindowToken() {
        return windowToken;
    }

    public IWindow getWindow() {
    public IWindow getWindow() {
        if (window != null) {
        if (window != null) {
            return window;
            return window;
+9 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package android.window;
package android.window;


import android.Manifest;
import android.annotation.RequiresPermission;
import android.graphics.Matrix;
import android.graphics.Matrix;
import android.util.Pair;
import android.util.Pair;
import android.util.Size;
import android.util.Size;
@@ -49,10 +51,13 @@ public abstract class WindowInfosListener {
    /**
    /**
     * Register the WindowInfosListener.
     * Register the WindowInfosListener.
     *
     *
     * Registering WindowInfosListeners should only be done within system_server and shell.
     *
     * @return The cached values for InputWindowHandles and DisplayInfos. This is the last updated
     * @return The cached values for InputWindowHandles and DisplayInfos. This is the last updated
     * value that was sent from SurfaceFlinger to this particular process. If there was nothing
     * value that was sent from SurfaceFlinger to this particular process. If there was nothing
     * registered previously, then the data can be empty.
     * registered previously, then the data can be empty.
     */
     */
    @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
    public Pair<InputWindowHandle[], DisplayInfo[]> register() {
    public Pair<InputWindowHandle[], DisplayInfo[]> register() {
        return nativeRegister(mNativeListener);
        return nativeRegister(mNativeListener);
    }
    }
@@ -65,8 +70,12 @@ public abstract class WindowInfosListener {
    }
    }


    private static native long nativeCreate(WindowInfosListener thiz);
    private static native long nativeCreate(WindowInfosListener thiz);

    @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
    private static native Pair<InputWindowHandle[], DisplayInfo[]> nativeRegister(long ptr);
    private static native Pair<InputWindowHandle[], DisplayInfo[]> nativeRegister(long ptr);

    private static native void nativeUnregister(long ptr);
    private static native void nativeUnregister(long ptr);

    private static native long nativeGetFinalizer();
    private static native long nativeGetFinalizer();


    /**
    /**
+136 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 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.window;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.InputConfig;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.view.InputWindowHandle;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;

/**
 * Wrapper class to provide access to WindowInfosListener within tests.
 *
 * @hide
 */
@TestApi
public class WindowInfosListenerForTest {

    /**
     * Window properties passed to {@code @WindowInfosListenerForTest#onWindowInfosChanged}.
     */
    public static class WindowInfo {
        /**
         * The window's token.
         */
        @NonNull
        public final IBinder windowToken;

        /**
         * The window's name.
         */
        @NonNull
        public final String name;

        /**
         * The window's position and size in display space.
         */
        @NonNull
        public final Rect bounds;

        WindowInfo(@NonNull IBinder windowToken, @NonNull String name, @NonNull Rect bounds) {
            this.windowToken = windowToken;
            this.name = name;
            this.bounds = bounds;
        }
    }

    private static final String TAG = "WindowInfosListenerForTest";

    private ArrayMap<Consumer<List<WindowInfo>>, WindowInfosListener> mListeners;

    public WindowInfosListenerForTest() {
        mListeners = new ArrayMap<>();
    }

    /**
     * Register a listener that is called when the system's list of visible windows has changes in
     * position or visibility.
     *
     * @param consumer Consumer that is called with reverse Z ordered lists of WindowInfo instances
     *                 where the first value is the topmost window.
     */
    @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
    public void addWindowInfosListener(
            @NonNull Consumer<List<WindowInfo>> consumer) {
        var calledWithInitialState = new CountDownLatch(1);
        var listener = new WindowInfosListener() {
            @Override
            public void onWindowInfosChanged(InputWindowHandle[] windowHandles,
                    DisplayInfo[] displayInfos) {
                try {
                    calledWithInitialState.await();
                } catch (InterruptedException exception) {
                    Log.e(TAG,
                            "Exception thrown while waiting for listener to be called with "
                                    + "initial state");
                }
                consumer.accept(buildWindowInfos(windowHandles));
            }
        };
        mListeners.put(consumer, listener);
        Pair<InputWindowHandle[], WindowInfosListener.DisplayInfo[]> initialState =
                listener.register();
        consumer.accept(buildWindowInfos(initialState.first));
        calledWithInitialState.countDown();
    }

    /**
     * Unregisters the listener.
     */
    public void removeWindowInfosListener(@NonNull Consumer<List<WindowInfo>> consumer) {
        WindowInfosListener listener = mListeners.remove(consumer);
        if (listener == null) {
            return;
        }
        listener.unregister();
    }

    private static List<WindowInfo> buildWindowInfos(InputWindowHandle[] windowHandles) {
        var windowInfos = new ArrayList<WindowInfo>(windowHandles.length);
        for (var handle : windowHandles) {
            if ((handle.inputConfig & InputConfig.NOT_VISIBLE) != 0) {
                continue;
            }
            var bounds = new Rect(handle.frameLeft, handle.frameTop, handle.frameRight,
                    handle.frameBottom);
            windowInfos.add(new WindowInfo(handle.getWindowToken(), handle.name, bounds));
        }
        return windowInfos;
    }
}