Fix offset bounds cut off display cutout region

Root cause :
Previous refactoring OneHandedDisplayAreaOrganizer and count on
mDisplayLayout.getStableBounds() for the rotated bounds, however
the bounds was cut off the safe inset(DisplayCutout) region.

Solution :
a. Do not count on DisplayLayout.getStableBounds() API
b. handling bounds by Surface.ROTATION_{N} with below
Rotate policy: 0 <-> 90, 0 <-> 270, 180 <-> 90, 180 <-> 270
| fromRotation  |  toRotation   |  rotateBounds |
-------------------------------------------------
|  ROTATION_0   |  ROTATION_90  |      True     |
|  ROTATION_0   |  ROTATION_270 |      True     |
|  ROTATION_180 |  ROTATION_90  |      True     |
|  ROTATION_180 |  ROTATION_270 |      True     |
|  ROTATION_90  |  ROTATION_0   |      True     |
|  ROTATION_90  |  ROTATION_180 |      True     |
|  ROTATION_270 |  ROTATION_0   |      True     |
|  ROTATION_270 |  ROTATION_180 |      True     |
|            Others             |      False    |

c. Reset bounds while register and unregister DisplayAreaOrganizer

Test: atest com.android.systemui.onehanded
Test: atest SystemUITests
Test: atest OneHandedDisplayAreaOrganizerTest
Bug: 160763351
Change-Id: I458edfe77c45320cd0d5b6dba2397b7229bcb048
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
index 28d7049..16e05f1 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -22,14 +22,12 @@
 import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
-import android.view.Display;
-import android.view.DisplayInfo;
+import android.view.Surface;
 import android.view.SurfaceControl;
 import android.window.DisplayAreaInfo;
 import android.window.DisplayAreaOrganizer;
@@ -42,7 +40,6 @@
 import com.android.internal.os.SomeArgs;
 import com.android.systemui.Dumpable;
 import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayLayout;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -83,8 +80,6 @@
     @VisibleForTesting
     HashMap<DisplayAreaInfo, SurfaceControl> mDisplayAreaMap = new HashMap();
     private DisplayController mDisplayController;
-    private DisplayLayout mDisplayLayout;
-    private DisplayInfo mDisplayInfo = new DisplayInfo();
     private OneHandedAnimationController mAnimationController;
     private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
             mSurfaceControlTransactionFactory;
@@ -129,6 +124,7 @@
         switch (msg.what) {
             case MSG_RESET_IMMEDIATE:
                 resetWindowsOffset();
+                mDefaultDisplayBounds.set(currentBounds);
                 mLastVisualDisplayBounds.set(currentBounds);
                 finishOffset(0, TRANSITION_DIRECTION_EXIT);
                 break;
@@ -157,9 +153,8 @@
         mUpdateHandler = new Handler(OneHandedThread.get().getLooper(), mUpdateCallback);
         mAnimationController = animationController;
         mDisplayController = displayController;
-        mDisplayLayout = getDisplayLayout(context);
-        mDisplayLayout.getStableBounds(mDefaultDisplayBounds);
-        mLastVisualDisplayBounds.set(mDefaultDisplayBounds);
+        mDefaultDisplayBounds.set(getDisplayBounds());
+        mLastVisualDisplayBounds.set(getDisplayBounds());
         mEnterExitAnimationDurationMs = context.getResources().getInteger(
                 com.android.systemui.R.integer.config_one_handed_translate_animation_duration);
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
@@ -175,6 +170,8 @@
             Log.w(TAG, "Bypass onDisplayAreaAppeared()! displayAreaInfo=" + displayAreaInfo);
             return;
         }
+        // mDefaultDisplayBounds may out of date after removeDisplayChangingController()
+        mDefaultDisplayBounds.set(getDisplayBounds());
         mDisplayAreaMap.put(displayAreaInfo, leash);
     }
 
@@ -193,26 +190,35 @@
     @Override
     public void unregisterOrganizer() {
         super.unregisterOrganizer();
-        if (mDisplayAreaMap != null) {
-            mDisplayAreaMap.clear();
-        }
+        resetWindowsOffset();
+
+        // Ensure all cached instance are cleared after resetWindowsOffset
+        mUpdateHandler.post(() -> {
+            if (mDisplayAreaMap != null && !mDisplayAreaMap.isEmpty()) {
+                mDisplayAreaMap.clear();
+            }
+        });
     }
 
     /**
-     * Handler for display rotation changes.
+     * Handler for display rotation changes by below policy which
+     * handles 90 degree display rotation changes {@link Surface.Rotation}
+     *
      */
-    public void onRotateDisplay(Resources res, int toRotation) {
+    public void onRotateDisplay(int fromRotation, int toRotation) {
         // Stop one handed without animation and reset cropped size immediately
-        final Rect newBounds = new Rect();
-        mDisplayLayout.rotateTo(res, toRotation);
-        mDisplayLayout.getStableBounds(newBounds);
+        final Rect newBounds = new Rect(mDefaultDisplayBounds);
+        final boolean isOrientationDiff = Math.abs(fromRotation - toRotation) % 2 == 1;
 
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = newBounds;
-        args.argi1 = 0 /* xOffset */;
-        args.argi2 = 0 /* yOffset */;
-        args.argi3 = TRANSITION_DIRECTION_EXIT;
-        mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
+        if (isOrientationDiff) {
+            newBounds.set(newBounds.left, newBounds.top, newBounds.bottom, newBounds.right);
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = newBounds;
+            args.argi1 = 0 /* xOffset */;
+            args.argi2 = 0 /* yOffset */;
+            args.argi3 = TRANSITION_DIRECTION_EXIT;
+            mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
+        }
     }
 
     /**
@@ -314,12 +320,12 @@
     }
 
     @Nullable
-    private Point getDisplayBounds() {
+    private Rect getDisplayBounds() {
         Point realSize = new Point(0, 0);
         if (mDisplayController != null && mDisplayController.getDisplay(DEFAULT_DISPLAY) != null) {
             mDisplayController.getDisplay(DEFAULT_DISPLAY).getRealSize(realSize);
         }
-        return realSize;
+        return new Rect(0, 0, realSize.x, realSize.y);
     }
 
     @VisibleForTesting
@@ -327,16 +333,6 @@
         return mUpdateHandler;
     }
 
-    private DisplayLayout getDisplayLayout(Context context) {
-        final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY);
-        if (display != null) {
-            display.getDisplayInfo(mDisplayInfo);
-        } else {
-            Log.w(TAG, "get DEFAULT_DISPLAY return null");
-        }
-        return new DisplayLayout(mDisplayInfo, context.getResources(), false, false);
-    }
-
     /**
      * Register transition callback
      */
@@ -368,9 +364,5 @@
         pw.println(mLastVisualDisplayBounds);
         pw.print(innerPrefix + "getDisplayBounds()=");
         pw.println(getDisplayBounds());
-        if (mDisplayLayout != null) {
-            pw.print(innerPrefix + "mDisplayLayout(w, h)=");
-            pw.println(mDisplayLayout.width() + ", " + mDisplayLayout.height());
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
index 4dacdf3..586761b 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
@@ -95,7 +95,7 @@
     private final DisplayChangeController.OnDisplayChangingListener mRotationController =
             (display, fromRotation, toRotation, wct) -> {
                 if (mDisplayAreaOrganizer != null) {
-                    mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), toRotation);
+                    mDisplayAreaOrganizer.onRotateDisplay(fromRotation, toRotation);
                 }
             };
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 07a32a9..7cf351f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -47,8 +47,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -59,7 +59,6 @@
 
     DisplayAreaInfo mDisplayAreaInfo;
     Display mDisplay;
-    Handler mUpdateHandler;
     OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
     OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator;
     WindowContainerToken mToken;
@@ -76,6 +75,8 @@
     DisplayController mMockDisplayController;
     @Mock
     SurfaceControl mMockLeash;
+    @Spy
+    Handler mUpdateHandler;
 
     @Before
     public void setUp() throws Exception {
@@ -103,7 +104,7 @@
         mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
                 mMockDisplayController,
                 mMockAnimationController);
-        mUpdateHandler = Mockito.spy(mDisplayAreaOrganizer.getUpdateHandler());
+        mUpdateHandler = mDisplayAreaOrganizer.getUpdateHandler();
     }
 
     @Test
@@ -139,21 +140,139 @@
         final int xOffSet = 0;
         final int yOffSet = 100;
 
+        TestableLooper.get(this).processAllMessages();
         mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
         mDisplayAreaOrganizer.scheduleOffset(xOffSet, yOffSet);
 
         assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_OFFSET_ANIMATE)).isNotNull();
+                OneHandedDisplayAreaOrganizer.MSG_OFFSET_ANIMATE)).isEqualTo(true);
     }
 
     @Test
-    public void testResetImmediately() {
-        // To prevent mNativeObject of Surface is null in the test flow
+    public void testRotation_portraitToLandscape() {
         when(mMockLeash.isValid()).thenReturn(false);
-        mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), Surface.ROTATION_90);
+        // Rotate 0 -> 90
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90);
 
         assertThat(mUpdateHandler.hasMessages(
-                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isNotNull();
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+        // Rotate 0 -> 270
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_270);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+        // Rotate 180 -> 90
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_90);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+        // Rotate 180 -> 270
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_270);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
     }
 
+    @Test
+    public void testRotation_landscapeToPortrait() {
+        when(mMockLeash.isValid()).thenReturn(false);
+        // Rotate 90 -> 0
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_0);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+        // Rotate 90 -> 180
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_180);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+        // Rotate 270 -> 0
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_0);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+        // Rotate 270 -> 180
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_180);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+    }
+
+    @Test
+    public void testRotation_portraitToPortrait() {
+        when(mMockLeash.isValid()).thenReturn(false);
+        // Rotate 0 -> 0
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_0);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+        // Rotate 0 -> 180
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_180);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+        // Rotate 180 -> 180
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_180);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+        // Rotate 180 -> 180
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_0);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+    }
+
+    @Test
+    public void testRotation_landscapeToLandscape() {
+        when(mMockLeash.isValid()).thenReturn(false);
+        // Rotate 90 -> 90
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_90);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+        // Rotate 90 -> 270
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_270);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+        // Rotate 270 -> 270
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_270);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+        // Rotate 270 -> 90
+        TestableLooper.get(this).processAllMessages();
+        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_90);
+
+        assertThat(mUpdateHandler.hasMessages(
+                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+    }
 }