OSDN Git Service

GestureNav: Log exclusion rect heights
authorAdrian Roos <roosa@google.com>
Wed, 3 Jul 2019 16:31:46 +0000 (18:31 +0200)
committerAdrian Roos <roosa@google.com>
Mon, 5 Aug 2019 19:19:40 +0000 (21:19 +0200)
Logs the rejected and requested exclusion rect heights.

Test: adb shell device_config put android:window_manager key_system_gesture_exclusion_log_debounce_millis 1000
Bug: 135152789
Change-Id: Ie12640f4bf0a8b40b666e604188a0bedd85e8981
Exempt-From-Owner-Approval: DeviceConfig.java: Adding constant to WindowManager namespace.

core/java/android/provider/DeviceConfig.java
core/java/android/view/ISystemGestureExclusionListener.aidl
core/java/com/android/internal/widget/PointerLocationView.java
packages/SystemUI/shared/src/com/android/systemui/shared/system/SystemGestureExclusionListenerCompat.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
services/core/java/com/android/server/wm/DisplayContent.java
services/core/java/com/android/server/wm/DisplayPolicy.java
services/core/java/com/android/server/wm/WindowManagerService.java
services/core/java/com/android/server/wm/WindowState.java
services/core/java/com/android/server/wm/WindowSurfaceController.java
services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java

index 1fd212d..ea50ae8 100644 (file)
@@ -338,6 +338,20 @@ public final class DeviceConfig {
                 "system_gestures_excluded_by_pre_q_sticky_immersive";
 
         /**
+         * The minimum duration between gesture exclusion logging for a given window in
+         * milliseconds.
+         *
+         * Events that happen in-between will be silently dropped.
+         *
+         * A non-positive value disables logging.
+         *
+         * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER
+         * @hide
+         */
+        String KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS =
+                "system_gesture_exclusion_log_debounce_millis";
+
+        /**
          * Key for controlling which packages are explicitly blocked from running at refresh rates
          * higher than 60hz.
          *
index a032625..9c2f9a6 100644 (file)
@@ -28,7 +28,14 @@ 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.
+     *                               system gestures, in display coordinates. Certain restrictions
+     *                               might be applied such that apps don't get all the exclusions
+     *                               they request.
+     * @param systemGestureExclusionUnrestricted a {@code Region} where the app would like priority
+     *                               over the system gestures, in display coordinates, without
+     *                               any restrictions applied. Null if no restrictions have been
+     *                               applied.
      */
-    void onSystemGestureExclusionChanged(int displayId, in Region systemGestureExclusion);
+    void onSystemGestureExclusionChanged(int displayId, in Region systemGestureExclusion,
+            in Region systemGestureExclusionUnrestricted);
 }
\ No newline at end of file
index 9084f62..d48034b 100644 (file)
@@ -53,6 +53,12 @@ public class PointerLocationView extends View implements InputDeviceListener,
     // to plot alongside the default one.  Useful for testing and comparison purposes.
     private static final String ALT_STRATEGY_PROPERY_KEY = "debug.velocitytracker.alt";
 
+    /**
+     * If set to a positive value between 1-255, shows an overlay with the approved (red) and
+     * rejected (blue) exclusions.
+     */
+    private static final String GESTURE_EXCLUSION_PROP = "debug.pointerlocation.showexclusion";
+
     public static class PointerState {
         // Trace of previous points.
         private float[] mTraceX = new float[32];
@@ -138,8 +144,10 @@ public class PointerLocationView extends View implements InputDeviceListener,
     private final PointerCoords mTempCoords = new PointerCoords();
 
     private final Region mSystemGestureExclusion = new Region();
+    private final Region mSystemGestureExclusionRejected = new Region();
     private final Path mSystemGestureExclusionPath = new Path();
     private final Paint mSystemGestureExclusionPaint;
+    private final Paint mSystemGestureExclusionRejectedPaint;
 
     private final VelocityTracker mVelocity;
     private final VelocityTracker mAltVelocity;
@@ -190,6 +198,10 @@ public class PointerLocationView extends View implements InputDeviceListener,
         mSystemGestureExclusionPaint.setARGB(25, 255, 0, 0);
         mSystemGestureExclusionPaint.setStyle(Paint.Style.FILL_AND_STROKE);
 
+        mSystemGestureExclusionRejectedPaint = new Paint();
+        mSystemGestureExclusionRejectedPaint.setARGB(25, 0, 0, 255);
+        mSystemGestureExclusionRejectedPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+
         PointerState ps = new PointerState();
         mPointers.add(ps);
         mActivePointerId = 0;
@@ -263,6 +275,12 @@ public class PointerLocationView extends View implements InputDeviceListener,
             canvas.drawPath(mSystemGestureExclusionPath, mSystemGestureExclusionPaint);
         }
 
+        if (!mSystemGestureExclusionRejected.isEmpty()) {
+            mSystemGestureExclusionPath.reset();
+            mSystemGestureExclusionRejected.getBoundaryPath(mSystemGestureExclusionPath);
+            canvas.drawPath(mSystemGestureExclusionPath, mSystemGestureExclusionRejectedPaint);
+        }
+
         // Labels
         if (mActivePointerId >= 0) {
             final PointerState ps = mPointers.get(mActivePointerId);
@@ -754,6 +772,9 @@ public class PointerLocationView extends View implements InputDeviceListener,
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
+            final int alpha = systemGestureExclusionOpacity();
+            mSystemGestureExclusionPaint.setAlpha(alpha);
+            mSystemGestureExclusionRejectedPaint.setAlpha(alpha);
         } else {
             mSystemGestureExclusion.setEmpty();
         }
@@ -805,7 +826,12 @@ public class PointerLocationView extends View implements InputDeviceListener,
     }
 
     private static boolean shouldShowSystemGestureExclusion() {
-        return SystemProperties.getBoolean("debug.pointerlocation.showexclusion", false);
+        return systemGestureExclusionOpacity() > 0;
+    }
+
+    private static int systemGestureExclusionOpacity() {
+        int x = SystemProperties.getInt(GESTURE_EXCLUSION_PROP, 0);
+        return x >= 0 && x <= 255 ? x : 0;
     }
 
     // HACK
@@ -928,12 +954,19 @@ public class PointerLocationView extends View implements InputDeviceListener,
     private ISystemGestureExclusionListener mSystemGestureExclusionListener =
             new ISystemGestureExclusionListener.Stub() {
         @Override
-        public void onSystemGestureExclusionChanged(int displayId, Region systemGestureExclusion) {
+        public void onSystemGestureExclusionChanged(int displayId, Region systemGestureExclusion,
+                Region systemGestureExclusionUnrestricted) {
             Region exclusion = Region.obtain(systemGestureExclusion);
+            Region rejected = Region.obtain();
+            if (systemGestureExclusionUnrestricted != null) {
+                rejected.set(systemGestureExclusionUnrestricted);
+                rejected.op(exclusion, Region.Op.DIFFERENCE);
+            }
             Handler handler = getHandler();
             if (handler != null) {
                 handler.post(() -> {
                     mSystemGestureExclusion.set(exclusion);
+                    mSystemGestureExclusionRejected.set(rejected);
                     exclusion.recycle();
                     invalidate();
                 });
index 9fdecfb..aeb0415 100644 (file)
@@ -34,9 +34,11 @@ public abstract class SystemGestureExclusionListenerCompat {
             new ISystemGestureExclusionListener.Stub() {
                 @Override
                 public void onSystemGestureExclusionChanged(int displayId,
-                        Region systemGestureExclusion) {
+                        Region systemGestureExclusion, Region unrestrictedOrNull) {
                     if (displayId == mDisplayId) {
-                        onExclusionChanged(systemGestureExclusion);
+                        Region unrestricted = (unrestrictedOrNull == null)
+                                ? systemGestureExclusion : unrestrictedOrNull;
+                        onExclusionChanged(systemGestureExclusion, unrestricted);
                     }
                 }
             };
@@ -47,11 +49,29 @@ public abstract class SystemGestureExclusionListenerCompat {
     }
 
     /**
-     * Called when the exclusion region has changed
+     * Called when the exclusion region has changed.
+     *
+     * TODO: remove, once all subclasses have migrated to
+     *       {@link #onExclusionChanged(Region, Region)}.
      */
     public abstract void onExclusionChanged(Region systemGestureExclusion);
 
     /**
+     * Called when the exclusion region has changed.
+     *
+     * @param systemGestureExclusion the system gesture exclusion to be applied
+     * @param systemGestureExclusionUnrestricted what would be the system gesture exclusion, if
+     *           there were no restrictions being applied. For logging purposes only.
+     *
+     */
+    public void onExclusionChanged(Region systemGestureExclusion,
+            Region systemGestureExclusionUnrestricted) {
+        // TODO: make abstract, once all subclasses have migrated away from
+        //       onExclusionChanged(Region)
+        onExclusionChanged(systemGestureExclusion);
+    }
+
+    /**
      * Registers the listener for getting exclusion rect changes.
      */
     public void register() {
index f9cdde8..e0c6c55 100644 (file)
@@ -103,7 +103,7 @@ public class EdgeBackGestureHandler implements DisplayListener {
             new ISystemGestureExclusionListener.Stub() {
                 @Override
                 public void onSystemGestureExclusionChanged(int displayId,
-                        Region systemGestureExclusion) {
+                        Region systemGestureExclusion, Region unrestrictedOrNull) {
                     if (displayId == mDisplayId) {
                         mMainExecutor.execute(() -> mExcludeRegion.set(systemGestureExclusion));
                     }
index 885095f..e099a4f 100644 (file)
@@ -67,6 +67,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
@@ -137,6 +138,8 @@ import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREEN
 import static com.android.server.wm.WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION;
 import static com.android.server.wm.WindowManagerService.dipToPixel;
 import static com.android.server.wm.WindowManagerService.logSurface;
+import static com.android.server.wm.WindowState.EXCLUSION_LEFT;
+import static com.android.server.wm.WindowState.EXCLUSION_RIGHT;
 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;
@@ -330,6 +333,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
     private final RemoteCallbackList<ISystemGestureExclusionListener>
             mSystemGestureExclusionListeners = new RemoteCallbackList<>();
     private final Region mSystemGestureExclusion = new Region();
+    private boolean mSystemGestureExclusionWasRestricted = false;
+    private final Region mSystemGestureExclusionUnrestricted = new Region();
     private int mSystemGestureExclusionLimit;
 
     /**
@@ -5139,16 +5144,21 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
             return false;
         }
 
-        final Region systemGestureExclusion = calculateSystemGestureExclusion();
+        final Region systemGestureExclusion = Region.obtain();
+        mSystemGestureExclusionWasRestricted = calculateSystemGestureExclusion(
+                systemGestureExclusion, mSystemGestureExclusionUnrestricted);
         try {
             if (mSystemGestureExclusion.equals(systemGestureExclusion)) {
                 return false;
             }
             mSystemGestureExclusion.set(systemGestureExclusion);
+            final Region unrestrictedOrNull = mSystemGestureExclusionWasRestricted
+                    ? mSystemGestureExclusionUnrestricted : null;
             for (int i = mSystemGestureExclusionListeners.beginBroadcast() - 1; i >= 0; --i) {
                 try {
                     mSystemGestureExclusionListeners.getBroadcastItem(i)
-                            .onSystemGestureExclusionChanged(mDisplayId, systemGestureExclusion);
+                            .onSystemGestureExclusionChanged(mDisplayId, systemGestureExclusion,
+                                    unrestrictedOrNull);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Failed to notify SystemGestureExclusionListener", e);
                 }
@@ -5160,8 +5170,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
         }
     }
 
+    /**
+     * Calculates the system gesture exclusion.
+     *
+     * @param outExclusion will be set to the gesture exclusion region
+     * @param outExclusionUnrestricted will be set to the gesture exclusion region without
+     *                                 any restrictions applied.
+     * @return whether any restrictions were applied, i.e. outExclusion and outExclusionUnrestricted
+     *         differ.
+     */
     @VisibleForTesting
-    Region calculateSystemGestureExclusion() {
+    boolean calculateSystemGestureExclusion(Region outExclusion, @Nullable
+            Region outExclusionUnrestricted) {
+        outExclusion.setEmpty();
+        if (outExclusionUnrestricted != null) {
+            outExclusionUnrestricted.setEmpty();
+        }
         final Region unhandled = Region.obtain();
         unhandled.set(0, 0, mDisplayFrames.mDisplayWidth, mDisplayFrames.mDisplayHeight);
 
@@ -5170,7 +5194,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
         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 =
@@ -5208,28 +5231,39 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
             if (needsGestureExclusionRestrictions(w, mLastDispatchedSystemUiVisibility)) {
 
                 // Processes the region along the left edge.
-                remainingLeftRight[0] = addToGlobalAndConsumeLimit(local, global, leftEdge,
-                        remainingLeftRight[0]);
+                remainingLeftRight[0] = addToGlobalAndConsumeLimit(local, outExclusion, leftEdge,
+                        remainingLeftRight[0], w, EXCLUSION_LEFT);
 
                 // Processes the region along the right edge.
-                remainingLeftRight[1] = addToGlobalAndConsumeLimit(local, global, rightEdge,
-                        remainingLeftRight[1]);
+                remainingLeftRight[1] = addToGlobalAndConsumeLimit(local, outExclusion, rightEdge,
+                        remainingLeftRight[1], w, EXCLUSION_RIGHT);
 
                 // 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);
+                outExclusion.op(middle, Op.UNION);
                 middle.recycle();
             } else {
-                global.op(local, Op.UNION);
+                boolean loggable = needsGestureExclusionRestrictions(w, 0 /* lastSysUiVis */);
+                if (loggable) {
+                    addToGlobalAndConsumeLimit(local, outExclusion, leftEdge,
+                            Integer.MAX_VALUE, w, EXCLUSION_LEFT);
+                    addToGlobalAndConsumeLimit(local, outExclusion, rightEdge,
+                            Integer.MAX_VALUE, w, EXCLUSION_RIGHT);
+                }
+                outExclusion.op(local, Op.UNION);
+            }
+            if (outExclusionUnrestricted != null) {
+                outExclusionUnrestricted.op(local, Op.UNION);
             }
             unhandled.op(touchableRegion, Op.DIFFERENCE);
         }, true /* topToBottom */);
         local.recycle();
         touchableRegion.recycle();
         unhandled.recycle();
-        return global;
+        return remainingLeftRight[0] < mSystemGestureExclusionLimit
+                || remainingLeftRight[1] < mSystemGestureExclusionLimit;
     }
 
     /**
@@ -5247,31 +5281,57 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
     }
 
     /**
+     * @return Whether gesture exclusion area should be logged for the given window
+     */
+    static boolean logsGestureExclusionRestrictions(WindowState win) {
+        if (win.mWmService.mSystemGestureExclusionLogDebounceTimeoutMillis <= 0) {
+            return false;
+        }
+        final WindowManager.LayoutParams attrs = win.getAttrs();
+        final int type = attrs.type;
+        return type != TYPE_WALLPAPER
+                && type != TYPE_APPLICATION_STARTING
+                && type != TYPE_NAVIGATION_BAR
+                && (attrs.flags & FLAG_NOT_TOUCHABLE) == 0
+                && needsGestureExclusionRestrictions(win, 0 /* sysUiVisibility */)
+                && win.getDisplayContent().mDisplayPolicy.hasSideGestures();
+    }
+
+    /**
      * 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.
+     * @param win The WindowState that is being processed
+     * @param side The side that is being processed, either {@link WindowState#EXCLUSION_LEFT} or
+     *             {@link WindowState#EXCLUSION_RIGHT}
+     * @return How much of the limit is remaining.
      */
     private static int addToGlobalAndConsumeLimit(Region local, Region global, Rect edge,
-            int limit) {
+            int limit, WindowState win, int side) {
         final Region r = Region.obtain(local);
         r.op(edge, Op.INTERSECT);
 
         final int[] remaining = {limit};
+        final int[] requestedExclusion = {0};
         forEachRectReverse(r, rect -> {
             if (remaining[0] <= 0) {
                 return;
             }
             final int height = rect.height();
+            requestedExclusion[0] += height;
             if (height > remaining[0]) {
                 rect.top = rect.bottom - remaining[0];
             }
             remaining[0] -= height;
             global.op(rect, Op.UNION);
         });
+
+        final int grantedExclusion = limit - remaining[0];
+        win.setLastExclusionHeights(side, requestedExclusion[0], grantedExclusion);
+
         r.recycle();
         return remaining[0];
     }
@@ -5286,10 +5346,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
         }
 
         if (!changed) {
+            final Region unrestrictedOrNull = mSystemGestureExclusionWasRestricted
+                    ? mSystemGestureExclusionUnrestricted : null;
             // If updateSystemGestureExclusion changed the exclusion, it will already have
             // notified the listener. Otherwise, we'll do it here.
             try {
-                listener.onSystemGestureExclusionChanged(mDisplayId, mSystemGestureExclusion);
+                listener.onSystemGestureExclusionChanged(mDisplayId, mSystemGestureExclusion,
+                        unrestrictedOrNull);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to notify SystemGestureExclusionListener during register", e);
             }
index cf87203..ed7dbd0 100644 (file)
@@ -478,9 +478,10 @@ public class DisplayPolicy {
 
                     @Override
                     public void onSwipeFromRight() {
-                        final Region excludedRegion;
+                        final Region excludedRegion = Region.obtain();
                         synchronized (mLock) {
-                            excludedRegion = mDisplayContent.calculateSystemGestureExclusion();
+                            mDisplayContent.calculateSystemGestureExclusion(
+                                    excludedRegion, null /* outUnrestricted */);
                         }
                         final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture
                                 || mNavigationBarPosition == NAV_BAR_RIGHT;
@@ -488,13 +489,15 @@ public class DisplayPolicy {
                                 && !mSystemGestures.currentGestureStartedInRegion(excludedRegion)) {
                             requestTransientBars(mNavigationBar);
                         }
+                        excludedRegion.recycle();
                     }
 
                     @Override
                     public void onSwipeFromLeft() {
-                        final Region excludedRegion;
+                        final Region excludedRegion = Region.obtain();
                         synchronized (mLock) {
-                            excludedRegion = mDisplayContent.calculateSystemGestureExclusion();
+                            mDisplayContent.calculateSystemGestureExclusion(
+                                    excludedRegion, null /* outUnrestricted */);
                         }
                         final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture
                                 || mNavigationBarPosition == NAV_BAR_LEFT;
@@ -502,6 +505,7 @@ public class DisplayPolicy {
                                 && !mSystemGestures.currentGestureStartedInRegion(excludedRegion)) {
                             requestTransientBars(mNavigationBar);
                         }
+                        excludedRegion.recycle();
                     }
 
                     @Override
@@ -674,6 +678,10 @@ public class DisplayPolicy {
         return mHasStatusBar;
     }
 
+    boolean hasSideGestures() {
+        return mHasNavigationBar && mSideGestureInset > 0;
+    }
+
     public boolean navigationBarCanMove() {
         return mNavigationBarCanMove;
     }
index 3ad6ddc..c108752 100644 (file)
@@ -34,6 +34,7 @@ import static android.os.Process.myPid;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE;
 import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP;
+import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS;
 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;
@@ -847,6 +848,16 @@ public class WindowManagerService extends IWindowManager.Stub
     int mSystemGestureExclusionLimitDp;
     boolean mSystemGestureExcludedByPreQStickyImmersive;
 
+    /**
+     * The minimum duration between gesture exclusion logging for a given window in
+     * milliseconds.
+     *
+     * Events that happen in-between will be silently dropped.
+     *
+     * A non-positive value disables logging.
+     */
+    public long mSystemGestureExclusionLogDebounceTimeoutMillis;
+
     public interface WindowChangeListener {
         public void windowsChanged();
         public void focusChanged();
@@ -1146,6 +1157,9 @@ public class WindowManagerService extends IWindowManager.Stub
         mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
                 DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
                         KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
+        mSystemGestureExclusionLogDebounceTimeoutMillis =
+                DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                        KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS, 0);
         mSystemGestureExcludedByPreQStickyImmersive =
                 DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
                         KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
@@ -1163,6 +1177,10 @@ public class WindowManagerService extends IWindowManager.Stub
                             mSystemGestureExcludedByPreQStickyImmersive = excludedByPreQSticky;
                             mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
                         }
+
+                        mSystemGestureExclusionLogDebounceTimeoutMillis =
+                                DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                                        KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS, 0);
                     }
                 });
 
index f68d7c0..18cdc94 100644 (file)
@@ -20,6 +20,8 @@ import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.OP_NONE;
+import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.os.PowerManager.DRAW_WAKE_LOCK;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -80,6 +82,7 @@ import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
 import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
 import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
 import static com.android.server.wm.AnimationSpecProto.MOVE;
+import static com.android.server.wm.DisplayContent.logsGestureExclusionRestrictions;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
@@ -174,6 +177,7 @@ import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.MergedConfiguration;
 import android.util.Slog;
+import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
@@ -227,6 +231,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
     // to capture touch events in that area.
     static final int RESIZE_HANDLE_WIDTH_IN_DP = 30;
 
+    static final int EXCLUSION_LEFT = 0;
+    static final int EXCLUSION_RIGHT = 1;
+
     final WindowManagerPolicy mPolicy;
     final Context mContext;
     final Session mSession;
@@ -397,6 +404,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
      */
     private final List<Rect> mExclusionRects = new ArrayList<>();
 
+    // 0 = left, 1 = right
+    private final int[] mLastRequestedExclusionHeight = {0, 0};
+    private final int[] mLastGrantedExclusionHeight = {0, 0};
+    private final long[] mLastExclusionLogUptimeMillis = {0, 0};
+
+    private boolean mLastShownChangedReported;
+
     // If a window showing a wallpaper: the requested offset for the
     // wallpaper; if a wallpaper window: the currently applied offset.
     float mWallpaperX = -1;
@@ -679,6 +693,20 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
                 && mAppToken != null && mAppToken.mTargetSdk < Build.VERSION_CODES.Q;
     }
 
+    void setLastExclusionHeights(int side, int requested, int granted) {
+        boolean changed = mLastGrantedExclusionHeight[side] != granted
+                || mLastRequestedExclusionHeight[side] != requested;
+
+        if (changed) {
+            if (mLastShownChangedReported) {
+                logExclusionRestrictions(side);
+            }
+
+            mLastGrantedExclusionHeight[side] = granted;
+            mLastRequestedExclusionHeight[side] = requested;
+        }
+    }
+
     interface PowerManagerWrapper {
         void wakeUp(long time, @WakeReason int reason, String details);
 
@@ -2957,6 +2985,49 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
         mAnimatingExit = false;
     }
 
+    void onSurfaceShownChanged(boolean shown) {
+        if (mLastShownChangedReported == shown) {
+            return;
+        }
+        mLastShownChangedReported = shown;
+
+        if (shown) {
+            initExclusionRestrictions();
+        } else {
+            logExclusionRestrictions(EXCLUSION_LEFT);
+            logExclusionRestrictions(EXCLUSION_RIGHT);
+        }
+    }
+
+    private void logExclusionRestrictions(int side) {
+        if (!logsGestureExclusionRestrictions(this)
+                || SystemClock.uptimeMillis() < mLastExclusionLogUptimeMillis[side]
+                + mWmService.mSystemGestureExclusionLogDebounceTimeoutMillis) {
+            // Drop the log if we have just logged; this is okay, because what we would have logged
+            // was true only for a short duration.
+            return;
+        }
+
+        final long now = SystemClock.uptimeMillis();
+        final long duration = now - mLastExclusionLogUptimeMillis[side];
+        mLastExclusionLogUptimeMillis[side] = now;
+
+        final int requested = mLastRequestedExclusionHeight[side];
+        final int granted = mLastGrantedExclusionHeight[side];
+
+        StatsLog.write(StatsLog.EXCLUSION_RECT_STATE_CHANGED,
+                mAttrs.packageName, requested, requested - granted /* rejected */,
+                side + 1 /* Sides are 1-indexed in atoms.proto */,
+                (getConfiguration().orientation == ORIENTATION_LANDSCAPE),
+                isSplitScreenWindowingMode(getWindowingMode()), (int) duration);
+    }
+
+    private void initExclusionRestrictions() {
+        final long now = SystemClock.uptimeMillis();
+        mLastExclusionLogUptimeMillis[EXCLUSION_LEFT] = now;
+        mLastExclusionLogUptimeMillis[EXCLUSION_RIGHT] = now;
+    }
+
     @Override
     public boolean isDefaultDisplay() {
         final DisplayContent displayContent = getDisplayContent();
index bef0f81..53bbd70 100644 (file)
@@ -507,6 +507,8 @@ class WindowSurfaceController {
 
         mService.updateNonSystemOverlayWindowsVisibilityIfNeeded(mAnimator.mWin, surfaceShown);
 
+        mAnimator.mWin.onSurfaceShownChanged(surfaceShown);
+
         if (mWindowSession != null) {
             mWindowSession.onWindowSurfaceVisibilityChanged(this, mSurfaceShown, mWindowType);
         }
index 7cd097e..6889086 100644 (file)
@@ -756,7 +756,8 @@ public class DisplayContentTests extends WindowTestsBase {
         final ISystemGestureExclusionListener.Stub verifier =
                 new ISystemGestureExclusionListener.Stub() {
             @Override
-            public void onSystemGestureExclusionChanged(int displayId, Region actual) {
+            public void onSystemGestureExclusionChanged(int displayId, Region actual,
+                    Region unrestricted) {
                 Region expected = Region.obtain();
                 expected.set(10, 20, 30, 40);
                 assertEquals(expected, actual);
@@ -790,7 +791,14 @@ public class DisplayContentTests extends WindowTestsBase {
 
         final Region expected = Region.obtain();
         expected.set(20, 30, 40, 50);
-        assertEquals(expected, dc.calculateSystemGestureExclusion());
+        assertEquals(expected, calculateSystemGestureExclusion(dc));
+    }
+
+    private Region calculateSystemGestureExclusion(DisplayContent dc) {
+        Region out = Region.obtain();
+        Region unrestricted = Region.obtain();
+        dc.calculateSystemGestureExclusion(out, unrestricted);
+        return out;
     }
 
     @Test
@@ -814,7 +822,7 @@ public class DisplayContentTests extends WindowTestsBase {
         win2.setHasSurface(true);
 
         final Region expected = Region.obtain();
-        assertEquals(expected, dc.calculateSystemGestureExclusion());
+        assertEquals(expected, calculateSystemGestureExclusion(dc));
     }
 
     @Test
@@ -839,7 +847,7 @@ public class DisplayContentTests extends WindowTestsBase {
 
             final Region expected = Region.obtain();
             expected.set(dc.getBounds());
-            assertEquals(expected, dc.calculateSystemGestureExclusion());
+            assertEquals(expected, calculateSystemGestureExclusion(dc));
 
             win.setHasSurface(false);
         }