Gestures: Assemble per-window exclusion zones
Adds a facility to listen for per-window exclusion zones and
assembles them into a per-display exclusion zone ready for
consumption by interested parties.
Bug: 126360272
Test: atest DisplayContentTests
Change-Id: Ic186f86ca551af98a5f77beb254d257c281a4160
diff --git a/Android.bp b/Android.bp
index e8f3561..fe9757f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -383,6 +383,7 @@
"core/java/android/view/IRecentsAnimationRunner.aidl",
"core/java/android/view/IRemoteAnimationFinishedCallback.aidl",
"core/java/android/view/IRotationWatcher.aidl",
+ "core/java/android/view/ISystemGestureExclusionListener.aidl",
"core/java/android/view/IWallpaperVisibilityListener.aidl",
"core/java/android/view/IWindow.aidl",
"core/java/android/view/IWindowFocusObserver.aidl",
diff --git a/core/java/android/view/ISystemGestureExclusionListener.aidl b/core/java/android/view/ISystemGestureExclusionListener.aidl
new file mode 100644
index 0000000..a032625
--- /dev/null
+++ b/core/java/android/view/ISystemGestureExclusionListener.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2019, 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.graphics.Region;
+
+/**
+ * Listener for changes to the system gesture exclusion region
+ *
+ * {@hide}
+ */
+oneway interface ISystemGestureExclusionListener {
+ /**
+ * Called when the system gesture exclusion for the given display changed.
+ * @param displayId the display whose system gesture exclusion changed
+ * @param systemGestureExclusion a {@code Region} where the app would like priority over the
+ * system gestures, in display coordinates.
+ */
+ void onSystemGestureExclusionChanged(int displayId, in Region systemGestureExclusion);
+}
\ No newline at end of file
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index e32c4e1..42f7d9e 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -40,6 +40,7 @@
import android.view.IPinnedStackListener;
import android.view.RemoteAnimationAdapter;
import android.view.IRotationWatcher;
+import android.view.ISystemGestureExclusionListener;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindowSession;
import android.view.IWindowSessionCallback;
@@ -281,6 +282,18 @@
int displayId);
/**
+ * Registers a system gesture exclusion listener for a given display.
+ */
+ void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener,
+ int displayId);
+
+ /**
+ * Unregisters a system gesture exclusion listener for a given display.
+ */
+ void unregisterSystemGestureExclusionListener(ISystemGestureExclusionListener listener,
+ int displayId);
+
+ /**
* Used only for assist -- request a screenshot of the current application.
*/
boolean requestAssistScreenshot(IAssistDataReceiver receiver);
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 1fcd432..87efb3f 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -32,6 +32,8 @@
import android.view.Surface;
import android.view.SurfaceControl;
+import java.util.List;
+
/**
* System private per-application interface to the window manager.
*
@@ -265,4 +267,10 @@
* that new state.
*/
void insetsModified(IWindow window, in InsetsState state);
+
+
+ /**
+ * Called when the system gesture exclusion has changed.
+ */
+ void reportSystemGestureExclusionChanged(IWindow window, in List<Rect> exclusionRects);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9222bd6..14df87c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3990,7 +3990,11 @@
void systemGestureExclusionChanged() {
final List<Rect> rectsForWindowManager = mGestureExclusionTracker.computeChangedRects();
if (rectsForWindowManager != null) {
- // TODO Send to WM
+ try {
+ mWindowSession.reportSystemGestureExclusionChanged(mWindow, rectsForWindowManager);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
mAttachInfo.mTreeObserver
.dispatchOnSystemGestureExclusionRectsChanged(rectsForWindowManager);
}
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 4773e16..3205b5a 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -20,12 +20,16 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
+import android.graphics.Path;
import android.graphics.RectF;
+import android.graphics.Region;
import android.hardware.input.InputManager;
import android.hardware.input.InputManager.InputDeviceListener;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Log;
import android.util.Slog;
+import android.view.ISystemGestureExclusionListener;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -34,6 +38,7 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowInsets;
+import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import java.util.ArrayList;
@@ -124,12 +129,16 @@
private int mActivePointerId;
private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>();
private final PointerCoords mTempCoords = new PointerCoords();
-
+
+ private final Region mSystemGestureExclusion = new Region();
+ private final Path mSystemGestureExclusionPath = new Path();
+ private final Paint mSystemGestureExclusionPaint;
+
private final VelocityTracker mVelocity;
private final VelocityTracker mAltVelocity;
private final FasterStringBuilder mText = new FasterStringBuilder();
-
+
private boolean mPrintCoords = true;
public PointerLocationView(Context c) {
@@ -168,7 +177,11 @@
mPathPaint.setARGB(255, 0, 96, 255);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(1);
-
+
+ mSystemGestureExclusionPaint = new Paint();
+ mSystemGestureExclusionPaint.setARGB(25, 255, 0, 0);
+ mSystemGestureExclusionPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+
PointerState ps = new PointerState();
mPointers.add(ps);
mActivePointerId = 0;
@@ -236,6 +249,12 @@
final int NP = mPointers.size();
+ if (!mSystemGestureExclusion.isEmpty()) {
+ mSystemGestureExclusionPath.reset();
+ mSystemGestureExclusion.getBoundaryPath(mSystemGestureExclusionPath);
+ canvas.drawPath(mSystemGestureExclusionPath, mSystemGestureExclusionPaint);
+ }
+
// Labels
if (mActivePointerId >= 0) {
final PointerState ps = mPointers.get(mActivePointerId);
@@ -719,6 +738,12 @@
super.onAttachedToWindow();
mIm.registerInputDeviceListener(this, getHandler());
+ try {
+ WindowManagerGlobal.getWindowManagerService().registerSystemGestureExclusionListener(
+ mSystemGestureExclusionListener, mContext.getDisplayId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
logInputDevices();
}
@@ -727,6 +752,12 @@
super.onDetachedFromWindow();
mIm.unregisterInputDeviceListener(this);
+ try {
+ WindowManagerGlobal.getWindowManagerService().unregisterSystemGestureExclusionListener(
+ mSystemGestureExclusionListener, mContext.getDisplayId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
@Override
@@ -876,4 +907,17 @@
return oldLength;
}
}
+
+ private ISystemGestureExclusionListener mSystemGestureExclusionListener =
+ new ISystemGestureExclusionListener.Stub() {
+ @Override
+ public void onSystemGestureExclusionChanged(int displayId, Region systemGestureExclusion) {
+ Region exclusion = Region.obtain(systemGestureExclusion);
+ getHandler().post(() -> {
+ mSystemGestureExclusion.set(exclusion);
+ exclusion.recycle();
+ invalidate();
+ });
+ }
+ };
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index db59030..b685a63 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -128,6 +128,7 @@
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.rectListToRegion;
import android.animation.AnimationHandler;
import android.annotation.CallSuper;
@@ -151,6 +152,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -163,6 +165,7 @@
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
+import android.view.ISystemGestureExclusionListener;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputWindowHandle;
@@ -308,6 +311,10 @@
private DisplayRotation mDisplayRotation;
DisplayFrames mDisplayFrames;
+ private final RemoteCallbackList<ISystemGestureExclusionListener>
+ mSystemGestureExclusionListeners = new RemoteCallbackList<>();
+ private final Region mSystemGestureExclusion = new Region();
+
/**
* For default display it contains real metrics, empty for others.
* @see WindowManagerService#createWatermarkInTransaction()
@@ -2789,6 +2796,14 @@
mWallpaperController.dump(pw, " ");
pw.println();
+ pw.print("mSystemGestureExclusion=");
+ if (mSystemGestureExclusionListeners.getRegisteredCallbackCount() > 0) {
+ pw.println(mSystemGestureExclusion);
+ } else {
+ pw.println("<no lstnrs>");
+ }
+
+ pw.println();
pw.println(prefix + "Application tokens in top down Z order:");
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
@@ -4914,6 +4929,100 @@
}
/**
+ * Updates the display's system gesture exclusion.
+ *
+ * @return true, if the exclusion changed.
+ */
+ boolean updateSystemGestureExclusion() {
+ if (mSystemGestureExclusionListeners.getRegisteredCallbackCount() == 0) {
+ // No one's interested anyways.
+ return false;
+ }
+
+ final Region systemGestureExclusion = calculateSystemGestureExclusion();
+ try {
+ if (mSystemGestureExclusion.equals(systemGestureExclusion)) {
+ return false;
+ }
+ mSystemGestureExclusion.set(systemGestureExclusion);
+ for (int i = mSystemGestureExclusionListeners.beginBroadcast() - 1; i >= 0; --i) {
+ try {
+ mSystemGestureExclusionListeners.getBroadcastItem(i)
+ .onSystemGestureExclusionChanged(mDisplayId, systemGestureExclusion);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify SystemGestureExclusionListener", e);
+ }
+ }
+ mSystemGestureExclusionListeners.finishBroadcast();
+ return true;
+ } finally {
+ systemGestureExclusion.recycle();
+ }
+ }
+
+ @VisibleForTesting
+ Region calculateSystemGestureExclusion() {
+ final Region global = Region.obtain();
+ final Region touchableRegion = Region.obtain();
+ final Region local = Region.obtain();
+
+ // 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) {
+ 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.
+ w.getTouchableRegion(touchableRegion);
+ global.op(touchableRegion, Op.DIFFERENCE);
+
+ rectListToRegion(w.getSystemGestureExclusion(), local);
+
+ // Transform to display coordinates
+ local.scale(w.mGlobalScale);
+ final Rect frame = w.getWindowFrames().mFrame;
+ local.translate(frame.left, frame.top);
+
+ // A window can only exclude system gestures where it is actually touchable
+ local.op(touchableRegion, Op.INTERSECT);
+
+ global.op(local, Op.UNION);
+ }, false /* topToBottom */);
+ local.recycle();
+ touchableRegion.recycle();
+ return global;
+ }
+
+ void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener) {
+ mSystemGestureExclusionListeners.register(listener);
+ final boolean changed;
+ if (mSystemGestureExclusionListeners.getRegisteredCallbackCount() == 1) {
+ changed = updateSystemGestureExclusion();
+ } else {
+ changed = false;
+ }
+
+ if (!changed) {
+ // If updateSystemGestureExclusion changed the exclusion, it will already have
+ // notified the listener. Otherwise, we'll do it here.
+ try {
+ listener.onSystemGestureExclusionChanged(mDisplayId, mSystemGestureExclusion);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify SystemGestureExclusionListener during register", e);
+ }
+ }
+ }
+
+ void unregisterSystemGestureExclusionListener(ISystemGestureExclusionListener listener) {
+ mSystemGestureExclusionListeners.unregister(listener);
+ }
+
+ /**
* Create a portal window handle for input. This window transports any touch to the display
* indicated by {@link InputWindowHandle#portalToDisplayId} if the touch hits this window.
*
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 300cd17..1167d6f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -654,6 +654,7 @@
// Finally update all input windows now that the window changes have stabilized.
forAllDisplays(dc -> {
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
+ dc.updateSystemGestureExclusion();
});
mWmService.setHoldScreenLocked(mHoldScreen);
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index dc8c7b7..9b634f9 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -57,6 +57,7 @@
import java.io.PrintWriter;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -314,6 +315,16 @@
}
}
+ @Override
+ public void reportSystemGestureExclusionChanged(IWindow window, List<Rect> exclusionRects) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mService.reportSystemGestureExclusionChanged(this, window, exclusionRects);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
private void actionOnWallpaper(IBinder window,
BiConsumer<WallpaperController, WindowState> action) {
final WindowState windowState = mService.windowForClientLocked(this, window, true);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e3a8be5..a1e6eea 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -202,6 +202,7 @@
import android.view.IPinnedStackListener;
import android.view.IRecentsAnimationRunner;
import android.view.IRotationWatcher;
+import android.view.ISystemGestureExclusionListener;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindow;
import android.view.IWindowId;
@@ -274,6 +275,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
+import java.util.List;
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
@@ -3788,6 +3790,42 @@
}
@Override
+ public void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener,
+ int displayId) {
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ throw new IllegalArgumentException("Trying to register visibility event "
+ + "for invalid display: " + displayId);
+ }
+ displayContent.registerSystemGestureExclusionListener(listener);
+ }
+ }
+
+ @Override
+ public void unregisterSystemGestureExclusionListener(ISystemGestureExclusionListener listener,
+ int displayId) {
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ throw new IllegalArgumentException("Trying to register visibility event "
+ + "for invalid display: " + displayId);
+ }
+ displayContent.unregisterSystemGestureExclusionListener(listener);
+ }
+ }
+
+ void reportSystemGestureExclusionChanged(Session session, IWindow window,
+ List<Rect> exclusionRects) {
+ synchronized (mGlobalLock) {
+ final WindowState win = windowForClientLocked(session, window, true);
+ if (win.setSystemGestureExclusion(exclusionRects)) {
+ win.getDisplayContent().updateSystemGestureExclusion();
+ }
+ }
+ }
+
+ @Override
public void registerDisplayFoldListener(IDisplayFoldListener listener) {
mPolicy.registerDisplayFoldListener(listener);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 20cca66..dd9e590 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -205,6 +205,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.List;
import java.util.function.Predicate;
/** A window in the window manager. */
@@ -363,6 +364,13 @@
*/
private final Rect mInsetFrame = new Rect();
+ /**
+ * List of rects where system gestures should be ignored.
+ *
+ * Coordinates are relative to the window's position.
+ */
+ private final List<Rect> mExclusionRects = new ArrayList<>();
+
// If a window showing a wallpaper: the requested offset for the
// wallpaper; if a wallpaper window: the currently applied offset.
float mWallpaperX = -1;
@@ -612,6 +620,24 @@
}
}
+ List<Rect> getSystemGestureExclusion() {
+ return mExclusionRects;
+ }
+
+ /**
+ * Sets the system gesture exclusion rects.
+ *
+ * @return {@code true} if anything changed
+ */
+ boolean setSystemGestureExclusion(List<Rect> exclusionRects) {
+ if (mExclusionRects.equals(exclusionRects)) {
+ return false;
+ }
+ mExclusionRects.clear();
+ mExclusionRects.addAll(exclusionRects);
+ return true;
+ }
+
interface PowerManagerWrapper {
void wakeUp(long time, @WakeReason int reason, String details);
diff --git a/services/core/java/com/android/server/wm/utils/RegionUtils.java b/services/core/java/com/android/server/wm/utils/RegionUtils.java
new file mode 100644
index 0000000..1458440
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/RegionUtils.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 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 com.android.server.wm.utils;
+
+import android.graphics.Rect;
+import android.graphics.Region;
+
+import java.util.List;
+
+/**
+ * Utility methods to handle Regions.
+ */
+public class RegionUtils {
+
+ private RegionUtils() {}
+
+
+ /**
+ * Converts a list of rects into a {@code Region}.
+ *
+ * @param rects the list of rects to convert
+ * @param outRegion the Region to set to the list of rects
+ */
+ public static void rectListToRegion(List<Rect> rects, Region outRegion) {
+ outRegion.setEmpty();
+ final int n = rects.size();
+ for (int i = 0; i < n; i++) {
+ outRegion.union(rects.get(i));
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index b26aa05..51dac6f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -25,6 +25,8 @@
import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -57,12 +59,15 @@
import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.graphics.Region;
import android.metrics.LogMaker;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
+import android.util.MutableBoolean;
import android.view.DisplayCutout;
import android.view.Gravity;
+import android.view.ISystemGestureExclusionListener;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.ViewRootImpl;
@@ -673,6 +678,60 @@
}
@Test
+ public void testUpdateSystemGestureExclusion() throws Exception {
+ final DisplayContent dc = createNewDisplay();
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
+ win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40)));
+
+ dc.setLayoutNeeded();
+ dc.performLayout(true /* initial */, false /* updateImeWindows */);
+
+ win.setHasSurface(true);
+ dc.updateSystemGestureExclusion();
+
+ final MutableBoolean invoked = new MutableBoolean(false);
+ final ISystemGestureExclusionListener.Stub verifier =
+ new ISystemGestureExclusionListener.Stub() {
+ @Override
+ public void onSystemGestureExclusionChanged(int displayId, Region actual) {
+ Region expected = Region.obtain();
+ expected.set(10, 20, 30, 40);
+ assertEquals(expected, actual);
+ invoked.value = true;
+ }
+ };
+ try {
+ dc.registerSystemGestureExclusionListener(verifier);
+ } finally {
+ dc.unregisterSystemGestureExclusionListener(verifier);
+ }
+ assertTrue("SystemGestureExclusionListener was not invoked", invoked.value);
+ }
+
+ @Test
+ public void testCalculateSystemGestureExclusion() throws Exception {
+ final DisplayContent dc = createNewDisplay();
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
+ win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40)));
+
+ final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "win2");
+ win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ win2.setSystemGestureExclusion(Collections.singletonList(new Rect(20, 30, 40, 50)));
+
+ dc.setLayoutNeeded();
+ dc.performLayout(true /* initial */, false /* updateImeWindows */);
+
+ win.setHasSurface(true);
+ win2.setHasSurface(true);
+
+ final Region expected = Region.obtain();
+ expected.set(20, 30, 40, 50);
+ assertEquals(expected, dc.calculateSystemGestureExclusion());
+ }
+
+ @Test
public void testOrientationChangeLogging() {
MetricsLogger mockLogger = mock(MetricsLogger.class);
Configuration oldConfig = new Configuration();