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

Commit 94e80780 authored by Charles Chen's avatar Charles Chen Committed by Android (Google) Code Review
Browse files

Merge "Introduce WindowManagerWrapper" into main

parents 5caee228 ddc9a87c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -878,7 +878,7 @@ public abstract class Window {
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
        mWindowManager = wm.createLocalWindowManager(this);
    }

    void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
+26 −0
Original line number Diff line number Diff line
@@ -6779,4 +6779,30 @@ public interface WindowManager extends ViewManager {
            @NonNull Consumer<@ScreenRecordingState Integer> callback) {
        throw new UnsupportedOperationException();
    }

    /**
     * Sets the parent window to this {@code WindowManager}.
     * This is necessary to attach sub-windows.
     *
     * @param parentWindow the parent window to be attached.
     *
     * @see android.window.WindowContext#attachWindow(View)
     *
     * @hide
     */
    default void setParentWindow(@NonNull Window parentWindow) {
        throw new UnsupportedOperationException();
    }

    /**
     * Creates a new instance of {@link WindowManager} with {@code parentWindow} attached.
     *
     * @param parentWindow the parent window to be attached.
     * @return a new instance of {@link WindowManager} with {@code parentWindow} attached
     *
     * @hide
     */
    default WindowManager createLocalWindowManager(@NonNull Window parentWindow) {
        throw new UnsupportedOperationException();
    }
}
+10 −9
Original line number Diff line number Diff line
@@ -81,12 +81,18 @@ import java.util.function.IntConsumer;
 * provides a window manager for adding windows that are associated with that
 * activity -- the window manager will not normally allow you to add arbitrary
 * windows that are not associated with an activity.
 * <p>
 * Note that extending {@code WindowManagerImpl} for {@link WindowManager} customization may lead to
 * crashes since {@link Window} and {@link WindowContext} may also customize
 * {@code WindowManagerImpl}, such as providing {@link #mParentWindow}
 * or {@link #mWindowContextToken}. Users should customize {@link WindowManager} via
 * {@link WindowManagerWrapper}.
 *
 * @see WindowManager
 * @see WindowManagerGlobal
 * @hide
 */
public class WindowManagerImpl implements WindowManager {
public final class WindowManagerImpl implements WindowManager {
    private static final String TAG = "WindowManager";

    @UnsupportedAppUsage
@@ -129,14 +135,11 @@ public class WindowManagerImpl implements WindowManager {
        mWindowMetricsController = new WindowMetricsController(mContext);
    }

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
    @Override
    public WindowManager createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);
    }

    public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
        return new WindowManagerImpl(displayContext, mParentWindow, mWindowContextToken);
    }

    /** Creates a {@link WindowManager} for a {@link WindowContext}. */
    public static WindowManager createWindowContextWindowManager(Context context) {
        final IBinder clientToken = context.getWindowContextToken();
@@ -153,9 +156,7 @@ public class WindowManagerImpl implements WindowManager {
        mDefaultToken = token;
    }

    /**
     * Sets the parent window.
     */
    @Override
    public void setParentWindow(@NonNull Window parentWindow) {
        mParentWindow = parentWindow;
    }
+328 −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 android.view;

import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT;

import android.Manifest;
import android.annotation.FlaggedApi;
import android.annotation.RequiresPermission;
import android.content.ComponentName;
import android.graphics.Bitmap;
import android.graphics.Region;
import android.os.IBinder;
import android.os.Looper;
import android.window.InputTransferToken;
import android.window.TaskFpsCallback;
import android.window.TrustedPresentationThresholds;

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

import com.android.window.flags.Flags;

import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.IntConsumer;

/**
 * A wrapper that wraps a base {@link WindowManager}.
 * <p>
 * Similar to {@link android.content.ContextWrapper}, it enables to customize behavior without
 * touching the original {@link WindowManager}.
 *
 * @hide
 */
public class WindowManagerWrapper implements WindowManager {

    @NonNull
    private final WindowManager mBase;

    public WindowManagerWrapper(@NonNull WindowManager base) {
        mBase = base;
    }

    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mBase.addView(view, params);
    }

    @Override
    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        mBase.updateViewLayout(view, params);
    }

    @Override
    public void removeView(View view) {
        mBase.removeView(view);
    }

    @Deprecated
    @Override
    public Display getDefaultDisplay() {
        return mBase.getDefaultDisplay();
    }

    @Override
    public void removeViewImmediate(View view) {
        mBase.removeViewImmediate(view);
    }

    @NonNull
    @Override
    public WindowMetrics getCurrentWindowMetrics() {
        return mBase.getCurrentWindowMetrics();
    }

    @NonNull
    @Override
    public WindowMetrics getMaximumWindowMetrics() {
        return mBase.getMaximumWindowMetrics();
    }

    @NonNull
    @Override
    public Set<WindowMetrics> getPossibleMaximumWindowMetrics(int displayId) {
        return mBase.getPossibleMaximumWindowMetrics(displayId);
    }

    @Override
    public void requestAppKeyboardShortcuts(KeyboardShortcutsReceiver receiver,
            int deviceId) {
        mBase.requestAppKeyboardShortcuts(receiver, deviceId);
    }

    @Override
    public KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId) {
        return mBase.getApplicationLaunchKeyboardShortcuts(deviceId);
    }

    @Override
    public void requestImeKeyboardShortcuts(KeyboardShortcutsReceiver receiver, int deviceId) {
        mBase.requestImeKeyboardShortcuts(receiver, deviceId);
    }

    @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
    @Override
    public Region getCurrentImeTouchRegion() {
        return mBase.getCurrentImeTouchRegion();
    }

    @Override
    public void setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow) {
        mBase.setShouldShowWithInsecureKeyguard(displayId, shouldShow);
    }

    @Override
    public void setShouldShowSystemDecors(int displayId, boolean shouldShow) {
        mBase.setShouldShowSystemDecors(displayId, shouldShow);
    }

    @Override
    public boolean shouldShowSystemDecors(int displayId) {
        return mBase.shouldShowSystemDecors(displayId);
    }

    @Override
    public void setDisplayImePolicy(int displayId, int imePolicy) {
        mBase.setDisplayImePolicy(displayId, imePolicy);
    }

    @FlaggedApi(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
    @Override
    public boolean isEligibleForDesktopMode(int displayId) {
        return mBase.isEligibleForDesktopMode(displayId);
    }

    @Override
    public int getDisplayImePolicy(int displayId) {
        return mBase.getDisplayImePolicy(displayId);
    }

    @Override
    public boolean isGlobalKey(int keyCode) {
        return mBase.isGlobalKey(keyCode);
    }

    @Override
    public boolean isCrossWindowBlurEnabled() {
        return mBase.isCrossWindowBlurEnabled();
    }

    @Override
    public void addCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
        mBase.addCrossWindowBlurEnabledListener(listener);
    }

    @Override
    public void addCrossWindowBlurEnabledListener(@NonNull Executor executor,
            @NonNull Consumer<Boolean> listener) {
        mBase.addCrossWindowBlurEnabledListener(executor, listener);
    }

    @Override
    public void removeCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
        mBase.removeCrossWindowBlurEnabledListener(listener);
    }

    @Override
    public void addProposedRotationListener(@NonNull Executor executor,
            @NonNull IntConsumer listener) {
        mBase.addProposedRotationListener(executor, listener);
    }

    @Override
    public void removeProposedRotationListener(@NonNull IntConsumer listener) {
        mBase.removeProposedRotationListener(listener);
    }

    @Override
    public void holdLock(IBinder token, int durationMs) {
        mBase.holdLock(token, durationMs);
    }

    @Override
    public boolean isTaskSnapshotSupported() {
        return mBase.isTaskSnapshotSupported();
    }

    @Override
    public void registerTaskFpsCallback(int taskId, @NonNull Executor executor,
            @NonNull TaskFpsCallback callback) {
        mBase.registerTaskFpsCallback(taskId, executor, callback);
    }

    @Override
    public void unregisterTaskFpsCallback(@NonNull TaskFpsCallback callback) {
        mBase.unregisterTaskFpsCallback(callback);
    }

    @Nullable
    @Override
    public Bitmap snapshotTaskForRecents(int taskId) {
        return mBase.snapshotTaskForRecents(taskId);
    }

    @NonNull
    @Override
    public List<ComponentName> notifyScreenshotListeners(int displayId) {
        return mBase.notifyScreenshotListeners(displayId);
    }

    @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
    @Override
    public boolean replaceContentOnDisplayWithMirror(int displayId, @NonNull Window window) {
        return mBase.replaceContentOnDisplayWithMirror(displayId, window);
    }

    @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
    @Override
    public boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) {
        return mBase.replaceContentOnDisplayWithSc(displayId, sc);
    }

    @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
    @Override
    public void registerTrustedPresentationListener(@NonNull IBinder window,
            @NonNull TrustedPresentationThresholds thresholds, @NonNull Executor executor,
            @NonNull Consumer<Boolean> listener) {
        mBase.registerTrustedPresentationListener(window, thresholds, executor, listener);
    }

    @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
    @Override
    public void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) {
        mBase.unregisterTrustedPresentationListener(listener);
    }

    @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
    @NonNull
    @Override
    public InputTransferToken registerBatchedSurfaceControlInputReceiver(
            @NonNull InputTransferToken hostInputTransferToken,
            @NonNull SurfaceControl surfaceControl, @NonNull Choreographer choreographer,
            @NonNull SurfaceControlInputReceiver receiver) {
        return mBase.registerBatchedSurfaceControlInputReceiver(
                hostInputTransferToken, surfaceControl, choreographer, receiver);
    }

    @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
    @NonNull
    @Override
    public InputTransferToken registerUnbatchedSurfaceControlInputReceiver(
            @NonNull InputTransferToken hostInputTransferToken,
            @NonNull SurfaceControl surfaceControl, @NonNull Looper looper,
            @NonNull SurfaceControlInputReceiver receiver) {
        return mBase.registerUnbatchedSurfaceControlInputReceiver(
                hostInputTransferToken, surfaceControl, looper, receiver);
    }

    @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
    @Override
    public void unregisterSurfaceControlInputReceiver(@NonNull SurfaceControl surfaceControl) {
        mBase.unregisterSurfaceControlInputReceiver(surfaceControl);
    }

    @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
    @Nullable
    @Override
    public IBinder getSurfaceControlInputClientToken(@NonNull SurfaceControl surfaceControl) {
        return mBase.getSurfaceControlInputClientToken(surfaceControl);
    }

    @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
    @Override
    public boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken,
            @NonNull InputTransferToken transferToToken) {
        return mBase.transferTouchGesture(transferFromToken, transferToToken);
    }

    @NonNull
    @Override
    public IBinder getDefaultToken() {
        return mBase.getDefaultToken();
    }

    @FlaggedApi(Flags.FLAG_SCREEN_RECORDING_CALLBACKS)
    @RequiresPermission(Manifest.permission.DETECT_SCREEN_RECORDING)
    @Override
    public @ScreenRecordingState int addScreenRecordingCallback(@NonNull Executor executor,
            @NonNull Consumer<@ScreenRecordingState Integer> callback) {
        return mBase.addScreenRecordingCallback(executor, callback);
    }

    @FlaggedApi(Flags.FLAG_SCREEN_RECORDING_CALLBACKS)
    @RequiresPermission(Manifest.permission.DETECT_SCREEN_RECORDING)
    @Override
    public void removeScreenRecordingCallback(
            @NonNull Consumer<@ScreenRecordingState Integer> callback) {
        mBase.removeScreenRecordingCallback(callback);
    }

    @Override
    public WindowManager createLocalWindowManager(@NonNull Window parentWindow) {
        final WindowManager newBase = mBase.createLocalWindowManager(parentWindow);
        return new WindowManagerWrapper(newBase);
    }

    @Override
    public void setParentWindow(@NonNull Window parentWindow) {
        mBase.setParentWindow(parentWindow);
    }
}
+60 −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 android.view

import android.platform.test.annotations.Presubmit
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith

/**
 * Tests for the [WindowManagerWrapper].
 *
 * Build/Install/Run:
 * atest FrameworksCoreTests:WindowManagerWrapperTest
 */
@RunWith(AndroidJUnit4::class)
@SmallTest
@Presubmit
class WindowManagerWrapperTest {

    /**
     * Tests that all default methods from [WindowManager] are implemented.
     */
    @Test
    fun testWindowManagerWrapperImplementation() {
        val windowManagerInterface = WindowManager::class.java
        val wmInterfaceMethods = windowManagerInterface.methods
        val windowManagerWrapperClass = WindowManagerWrapper::class.java
        val wrapperMethodsFromWm = windowManagerWrapperClass.declaredMethods
            .filter { m -> windowManagerInterface.isAssignableFrom(m.declaringClass) }
            .map { m -> m.name }
            .toSet()

        // Only checks the default methods in WM interface. Missing implementation of non-default
        // methods should be caught at compile time.
        val wmDefaultMethods = wmInterfaceMethods
            .filter { m -> m.isDefault }
            .map { m -> m.name }
            .toList()

        for (defaultMethod in wmDefaultMethods) {
            assertThat(wrapperMethodsFromWm).contains(defaultMethod)
        }
    }
}
Loading