summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Adrian Roos <roosa@google.com> 2019-06-18 16:13:53 +0200
committer Tiger Huang <tigerhuang@google.com> 2019-06-28 18:13:12 +0800
commitbf3bc1b48364bb93789e75191fc3a9508018976c (patch)
tree9df786701387568b7c10ac549991adc82f9d864b
parent8fe7767d0d480f066e5af8ae5d892d1ccfe106aa (diff)
GestureNav: Limit exclusion rects
Add limits for system gesture exclusion on the left and right edges. This prevents non-immersive-sticky apps from totally disabling the back gesture. Bug: 135522625 Test: atest SystemGestureExclusionRectsTest Change-Id: Ib26161663a6aababe803d3c70044f4017bdbe675 Exempt-From-Owner-Approval: Already +2ed on another branch
-rw-r--r--api/test-current.txt5
-rw-r--r--core/java/android/provider/DeviceConfig.java26
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java113
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java22
-rw-r--r--services/core/java/com/android/server/wm/utils/RegionUtils.java16
-rw-r--r--services/tests/wmtests/AndroidManifest.xml1
6 files changed, 175 insertions, 8 deletions
diff --git a/api/test-current.txt b/api/test-current.txt
index bf2730abcb99..fe73fecb6b86 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2301,6 +2301,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 {
@@ -2317,6 +2318,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 01b6758f8b15..48faf114d2d7 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -293,6 +293,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
*
* @hide
@@ -309,6 +318,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<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 57ed92d8b794..72a7fcfe1e29 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;
@@ -323,6 +329,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private final RemoteCallbackList<ISystemGestureExclusionListener>
mSystemGestureExclusionListeners = new RemoteCallbackList<>();
private final Region mSystemGestureExclusion = new Region();
+ private int mSystemGestureExclusionLimit;
/**
* For default display it contains real metrics, empty for others.
@@ -893,6 +900,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mWallpaperController = new WallpaperController(mWmService, this);
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
+ mSystemGestureExclusionLimit = mWmService.mSystemGestureExclusionLimitDp
+ * mDisplayMetrics.densityDpi / DENSITY_DEFAULT;
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo,
calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
@@ -1548,8 +1557,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
longSize = height;
}
- final int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
- final int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
+ final int shortSizeDp = shortSize * DENSITY_DEFAULT / mBaseDisplayDensity;
+ final int longSizeDp = longSize * DENSITY_DEFAULT / mBaseDisplayDensity;
mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();
mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp);
@@ -2199,6 +2208,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
onDisplayChanged(this);
}
+ @Override
+ void onDisplayChanged(DisplayContent dc) {
+ super.onDisplayChanged(dc);
+ updateSystemGestureExclusionLimit();
+ }
+
+ void updateSystemGestureExclusionLimit() {
+ mSystemGestureExclusionLimit = mWmService.mSystemGestureExclusionLimitDp
+ * mDisplayMetrics.densityDpi / DENSITY_DEFAULT;
+ updateSystemGestureExclusion();
+ }
+
void initializeDisplayBaseInfo() {
final DisplayManagerInternal displayManagerInternal = mWmService.mDisplayManagerInternal;
if (displayManagerInternal != null) {
@@ -5130,24 +5151,35 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
@VisibleForTesting
Region calculateSystemGestureExclusion() {
+ final Region unhandled = Region.obtain();
+ unhandled.set(0, 0, mDisplayFrames.mDisplayWidth, mDisplayFrames.mDisplayHeight);
+
+ final Rect leftEdge = mInsetsStateController.getSourceProvider(TYPE_LEFT_GESTURES)
+ .getSource().getFrame();
+ final Rect rightEdge = mInsetsStateController.getSourceProvider(TYPE_RIGHT_GESTURES)
+ .getSource().getFrame();
+
final Region global = Region.obtain();
final Region touchableRegion = Region.obtain();
final Region local = Region.obtain();
+ final int[] remainingLeftRight =
+ {mSystemGestureExclusionLimit, mSystemGestureExclusionLimit};
// Traverse all windows bottom up to assemble the gesture exclusion rects.
// For each window, we only take the rects that fall within its touchable region.
forAllWindows(w -> {
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);
@@ -5159,13 +5191,78 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// A window can only exclude system gestures where it is actually touchable
local.op(touchableRegion, Op.INTERSECT);
- global.op(local, Op.UNION);
- }, false /* topToBottom */);
+ // Apply restriction if necessary.
+ if (needsGestureExclusionRestrictions(w, mLastDispatchedSystemUiVisibility)) {
+
+ // Processes the region along the left edge.
+ remainingLeftRight[0] = addToGlobalAndConsumeLimit(local, global, leftEdge,
+ remainingLeftRight[0]);
+
+ // Processes the region along the right edge.
+ remainingLeftRight[1] = addToGlobalAndConsumeLimit(local, global, rightEdge,
+ remainingLeftRight[1]);
+
+ // Adds the middle (unrestricted area)
+ final Region middle = Region.obtain(local);
+ middle.op(leftEdge, Op.DIFFERENCE);
+ middle.op(rightEdge, Op.DIFFERENCE);
+ global.op(middle, Op.UNION);
+ middle.recycle();
+ } else {
+ global.op(local, Op.UNION);
+ }
+ unhandled.op(touchableRegion, Op.DIFFERENCE);
+ }, true /* topToBottom */);
local.recycle();
touchableRegion.recycle();
+ unhandled.recycle();
return global;
}
+ /**
+ * @return Whether gesture exclusion area should be restricted from the window depending on the
+ * current SystemUI visibility flags.
+ */
+ private static boolean needsGestureExclusionRestrictions(WindowState win, int sysUiVisibility) {
+ final int type = win.mAttrs.type;
+ final int stickyHideNavFlags =
+ SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ final boolean stickyHideNav =
+ (sysUiVisibility & stickyHideNavFlags) == stickyHideNavFlags;
+ return !stickyHideNav && type != TYPE_INPUT_METHOD && type != TYPE_STATUS_BAR
+ && win.getActivityType() != ACTIVITY_TYPE_HOME;
+ }
+
+ /**
+ * Adds a local gesture exclusion area to the global area while applying a limit per edge.
+ *
+ * @param local The gesture exclusion area to add.
+ * @param global The destination.
+ * @param edge Only processes the part in that region.
+ * @param limit How much limit in pixels we have.
+ * @return How much of the limit are remaining.
+ */
+ private static int addToGlobalAndConsumeLimit(Region local, Region global, Rect edge,
+ int limit) {
+ final Region r = Region.obtain(local);
+ r.op(edge, Op.INTERSECT);
+
+ final int[] remaining = {limit};
+ forEachRect(r, rect -> {
+ 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 6750ece1861e..54f712cc6cef 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;
@@ -155,6 +156,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;
@@ -176,6 +178,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;
@@ -382,6 +385,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;
@@ -840,6 +845,8 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<WindowChangeListener> mWindowChangeListeners = new ArrayList<>();
boolean mWindowsChanged = false;
+ int mSystemGestureExclusionLimitDp;
+
public interface WindowChangeListener {
public void windowsChanged();
public void focusChanged();
@@ -1134,6 +1141,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 1458440f7b81..8cd6f8826083 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<Rect> 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 4c27a3ce0bf3..8c4544249b5c 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -36,6 +36,7 @@
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
<application android:debuggable="true"