diff --git a/api/test-current.txt b/api/test-current.txt index 74725bc26e1c1..1dc6faad37644 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -2294,6 +2294,7 @@ package android.provider { field public static final String NAMESPACE_PRIVACY = "privacy"; field public static final String NAMESPACE_ROLLBACK = "rollback"; field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot"; + field public static final String NAMESPACE_WINDOW_MANAGER = "android:window_manager"; } public static interface DeviceConfig.OnPropertiesChangedListener { @@ -2310,6 +2311,10 @@ package android.provider { method @Nullable public String getString(@NonNull String, @Nullable String); } + public static interface DeviceConfig.WindowManager { + field public static final String KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP = "system_gesture_exclusion_limit_dp"; + } + public final class MediaStore { method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static void deleteContributedMedia(android.content.Context, String, android.os.UserHandle) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static long getContributedMediaSize(android.content.Context, String, android.os.UserHandle) throws java.io.IOException; diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 19dbc6a2ec001..920eb4b517759 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -284,6 +284,15 @@ public final class DeviceConfig { */ public static final String NAMESPACE_SETTINGS_UI = "settings_ui"; + /** + * Namespace for window manager related features. The names to access the properties in this + * namespace should be defined in {@link WindowManager}. + * + * @hide + */ + @TestApi + public static final String NAMESPACE_WINDOW_MANAGER = "android:window_manager"; + /** * List of namespaces which can be read without READ_DEVICE_CONFIG permission * @@ -301,6 +310,23 @@ public final class DeviceConfig { @TestApi public static final String NAMESPACE_PRIVACY = "privacy"; + /** + * Interface for accessing keys belonging to {@link #NAMESPACE_WINDOW_MANAGER}. + * @hide + */ + @TestApi + public interface WindowManager { + + /** + * Key for accessing the system gesture exclusion limit (an integer in dp). + * + * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER + * @hide + */ + @TestApi + String KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP = "system_gesture_exclusion_limit_dp"; + } + private static final Object sLock = new Object(); @GuardedBy("sLock") private static ArrayMap> sSingleListeners = diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 80848a8f24f06..1659131d880db 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -30,16 +30,21 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.util.DisplayMetrics.DENSITY_DEFAULT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_PRIVATE; import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; import static android.view.Display.INVALID_DISPLAY; import static android.view.InsetsState.TYPE_IME; +import static android.view.InsetsState.TYPE_LEFT_GESTURES; +import static android.view.InsetsState.TYPE_RIGHT_GESTURES; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.View.GONE; +import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; +import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; import static android.view.WindowManager.DOCKED_BOTTOM; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_TOP; @@ -135,6 +140,7 @@ import static com.android.server.wm.WindowManagerService.logSurface; import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP; import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW; +import static com.android.server.wm.utils.RegionUtils.forEachRect; import static com.android.server.wm.utils.RegionUtils.rectListToRegion; import android.animation.AnimationHandler; @@ -324,6 +330,7 @@ class DisplayContent extends WindowContainer mSystemGestureExclusionListeners = new RemoteCallbackList<>(); private final Region mSystemGestureExclusion = new Region(); + private int mSystemGestureExclusionLimit; /** * For default display it contains real metrics, empty for others. @@ -894,6 +901,8 @@ class DisplayContent extends WindowContainer { if (w.cantReceiveTouchInput() || !w.isVisible() - || (w.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0) { + || (w.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0 + || unhandled.isEmpty()) { return; } final boolean modal = (w.mAttrs.flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0; - // Only keep the exclusion zones from the windows behind where the current window - // isn't touchable. + // Get the touchable region of the window, and intersect with where the screen is still + // touchable, i.e. touchable regions on top are not covering it yet. w.getTouchableRegion(touchableRegion); - global.op(touchableRegion, Op.DIFFERENCE); + touchableRegion.op(unhandled, Op.INTERSECT); rectListToRegion(w.getSystemGestureExclusion(), local); @@ -5136,13 +5168,78 @@ class DisplayContent extends WindowContainer { + if (remaining[0] <= 0) { + return; + } + final int height = rect.height(); + if (height > remaining[0]) { + rect.bottom = rect.top + remaining[0]; + } + remaining[0] -= height; + global.op(rect, Op.UNION); + }); + r.recycle(); + return remaining[0]; + } + void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener) { mSystemGestureExclusionListeners.register(listener); final boolean changed; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 57fa2ed63121f..e36922d281062 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -32,6 +32,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Process.SYSTEM_UID; import static android.os.Process.myPid; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; @@ -154,6 +155,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.IBinder; import android.os.IRemoteCallback; import android.os.Looper; @@ -175,6 +177,7 @@ import android.os.SystemService; import android.os.Trace; import android.os.UserHandle; import android.os.WorkSource; +import android.provider.DeviceConfig; import android.provider.Settings; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; @@ -380,6 +383,8 @@ public class WindowManagerService extends IWindowManager.Stub private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000; + private static final int MIN_GESTURE_EXCLUSION_LIMIT_DP = 200; + final WindowTracing mWindowTracing; final private KeyguardDisableHandler mKeyguardDisableHandler; @@ -838,6 +843,8 @@ public class WindowManagerService extends IWindowManager.Stub final ArrayList mWindowChangeListeners = new ArrayList<>(); boolean mWindowsChanged = false; + int mSystemGestureExclusionLimitDp; + public interface WindowChangeListener { public void windowsChanged(); public void focusChanged(); @@ -1132,6 +1139,21 @@ public class WindowManagerService extends IWindowManager.Stub this, mInputManager, mActivityTaskManager, mH.getLooper()); mDragDropController = new DragDropController(this, mH.getLooper()); + mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP, + DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0)); + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + new HandlerExecutor(mH), properties -> { + synchronized (mGlobalLock) { + final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP, + properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0)); + if (mSystemGestureExclusionLimitDp != exclusionLimitDp) { + mSystemGestureExclusionLimitDp = exclusionLimitDp; + mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit); + } + } + }); + LocalServices.addService(WindowManagerInternal.class, new LocalService()); } diff --git a/services/core/java/com/android/server/wm/utils/RegionUtils.java b/services/core/java/com/android/server/wm/utils/RegionUtils.java index 1458440f7b812..8cd6f88260833 100644 --- a/services/core/java/com/android/server/wm/utils/RegionUtils.java +++ b/services/core/java/com/android/server/wm/utils/RegionUtils.java @@ -18,8 +18,10 @@ package com.android.server.wm.utils; import android.graphics.Rect; import android.graphics.Region; +import android.graphics.RegionIterator; import java.util.List; +import java.util.function.Consumer; /** * Utility methods to handle Regions. @@ -42,4 +44,18 @@ public class RegionUtils { outRegion.union(rects.get(i)); } } + + /** + * Applies actions on each rect contained within a {@code Region}. + * + * @param region the given region. + * @param rectConsumer the action holder. + */ + public static void forEachRect(Region region, Consumer rectConsumer) { + final RegionIterator it = new RegionIterator(region); + final Rect rect = new Rect(); + while (it.next(rect)) { + rectConsumer.accept(rect); + } + } } diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 5136705ffbc25..2ce33fcc62b42 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -36,6 +36,7 @@ +