Sanitize window private flags based on caller permissions.

Introduces `sanitizePrivateFlags` to check if the calling application has the necessary permissions to set certain `LayoutParams.privateFlags`. Specifically, `PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY` and `PRIVATE_FLAG_TRUSTED_OVERLAY` require `INTERNAL_SYSTEM_WINDOW`, and `PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP` requires `MANAGE_ACTIVITY_TASKS`. These flags are removed if the caller lacks the required permissions during window relayout.

Bug: 406243581
Test: atest WindowManagerServiceTests
Flag: EXEMPT bug fix

(cherry picked from commit ec6a92be6c93173fb5c61f95ff92e0f68fe0e951)
Cherrypick-From: https://googleplex-android-review.googlesource.com/q/commit:3d9b1584cee3013a374b4bfbcd7a3a75723d5379
Merged-In: I4fdf2152f06082e13690e7f5b628e0ca0acdff84
Change-Id: I4fdf2152f06082e13690e7f5b628e0ca0acdff84
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d259870..27dce1c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2405,6 +2405,8 @@
             int privateFlagChanges = 0;
             if (attrs != null) {
                 displayPolicy.adjustWindowParamsLw(win, attrs);
+                attrs.privateFlags = sanitizePrivateFlags(attrs.privateFlags,
+                        win.mAttrs.privateFlags, win.getName(), uid, pid);
                 attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), uid, pid);
                 attrs.inputFeatures = sanitizeInputFeatures(attrs.inputFeatures, win.getName(), uid,
                         pid, win.isTrustedOverlay());
@@ -9290,6 +9292,42 @@
         return inputFeatures;
     }
 
+    private boolean hasFlags(int flags, int mask) {
+        return (flags & mask) != 0;
+    }
+
+    private boolean hasPermission(String permission, int callingPid, int callingUid) {
+        return mContext.checkPermission(permission, callingPid, callingUid)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    /**
+     * Ensure the caller has the right permissions to be able to set the requested private flags.
+     */
+    private int sanitizePrivateFlags(int newPrivateFlags, int oldPrivateFlags, String windowName,
+            int callingUid, int callingPid) {
+        final int addedPrivateFlags = ~oldPrivateFlags & newPrivateFlags;
+        int sanitizedFlags = newPrivateFlags;
+        if (hasFlags(addedPrivateFlags,
+                (PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY| PRIVATE_FLAG_TRUSTED_OVERLAY))
+                && !hasPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW,
+                    callingUid, callingPid)) {
+            Slog.w(TAG, "Removing PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY or"
+                    + " PRIVATE_FLAG_TRUSTED_OVERLAY from '" + windowName
+                    + "' because it doesn't have INTERNAL_SYSTEM_WINDOW permission");
+            sanitizedFlags &=
+                    ~(PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY | PRIVATE_FLAG_TRUSTED_OVERLAY);
+        }
+        if (hasFlags(addedPrivateFlags, LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP)
+                && !hasPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS, callingUid,
+                    callingPid)) {
+            Slog.w(TAG, "Removing PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP from '"
+                + windowName + "' because it doesn't have MANAGE_ACTIVITY_TASKS permission");
+            sanitizedFlags &= ~LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
+        }
+        return sanitizedFlags;
+    }
+
     /**
      * Assigns an InputChannel to a SurfaceControl and configures it to receive
      * touch input according to it's on-screen geometry.
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 9645e18..78e7523a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -32,6 +32,8 @@
 import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SENSITIVE_FOR_PRIVACY;
 import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
 import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -404,19 +406,135 @@
                 /*expectedPrivateFlagsValue=*/ 0);
     }
 
+    @Test
+    public void testRelayout_addTrustedOverlay_permissionDenied() {
+        testRelayoutFlagChanges(
+                false, /* firstRelayout */
+                0, /* startFlags */
+                0, /* startPrivateFlags */
+                0, /* newFlags */
+                PRIVATE_FLAG_TRUSTED_OVERLAY, /* newPrivateFlags */
+                0, /* expectedChangedFlags */
+                0, /* expectedChangedPrivateFlags */
+                0, /* expectedFlagsValue */
+                0, /* expectedPrivateFlagsValue */
+                false, /* internalSystemWindowGranted */
+                true /* manageActivityTasksGranted */);
+    }
+
+    @Test
+    public void testRelayout_addTrustedOverlay_permissionGranted() {
+        testRelayoutFlagChanges(
+                false, /* firstRelayout */
+                0, /* startFlags */
+                0, /* startPrivateFlags */
+                0, /* newFlags */
+                PRIVATE_FLAG_TRUSTED_OVERLAY, /* newPrivateFlags */
+                0, /* expectedChangedFlags */
+                PRIVATE_FLAG_TRUSTED_OVERLAY, /* expectedChangedPrivateFlags */
+                0, /* expectedFlagsValue */
+                PRIVATE_FLAG_TRUSTED_OVERLAY /* expectedPrivateFlagsValue */,
+                true, /* internalSystemWindowGranted */
+                true /* manageActivityTasksGranted */);
+    }
+
+    @Test
+    public void testRelayout_addRoundedCornersOverlay_permissionDenied() {
+        testRelayoutFlagChanges(
+                false, /* firstRelayout */
+                0, /* startFlags */
+                0, /* startPrivateFlags */
+                0, /* newFlags */
+                PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY, /* newPrivateFlags */
+                0, /* expectedChangedFlags */
+                0, /* expectedChangedPrivateFlags */
+                0, /* expectedFlagsValue */
+                0, /* expectedPrivateFlagsValue */
+                false, /* internalSystemWindowGranted */
+                true /* manageActivityTasksGranted */);
+    }
+
+    @Test
+    public void testRelayout_addRoundedCornersOverlay_permissionGranted() {
+        testRelayoutFlagChanges(
+                false, /* firstRelayout */
+                0, /* startFlags */
+                0, /* startPrivateFlags */
+                0, /* newFlags */
+                PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY, /* newPrivateFlags */
+                0, /* expectedChangedFlags */
+                PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY, /* expectedChangedPrivateFlags */
+                0, /* expectedFlagsValue */
+                PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY /* expectedPrivateFlagsValue */,
+                true, /* internalSystemWindowGranted */
+                true /* manageActivityTasksGranted */);
+    }
+
+    @Test
+    public void testRelayout_addInterceptGlobalDragAndDrop_permissionDenied() {
+        testRelayoutFlagChanges(
+                false, /* firstRelayout */
+                0, /* startFlags */
+                0, /* startPrivateFlags */
+                0, /* newFlags */
+                PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP, /* newPrivateFlags */
+                0, /* expectedChangedFlags */
+                0, /* expectedChangedPrivateFlags */
+                0, /* expectedFlagsValue */
+                0, /* expectedPrivateFlagsValue */
+                true, /* internalSystemWindowGranted */
+                false /* manageActivityTasksGranted */);
+    }
+
+    @Test
+    public void testRelayout_addInterceptGlobalDragAndDrop_permissionGranted() {
+        testRelayoutFlagChanges(
+                false, /* firstRelayout */
+                0, /* startFlags */
+                0, /* startPrivateFlags */
+                0, /* newFlags */
+                PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP, /* newPrivateFlags */
+                0, /* expectedChangedFlags */
+                PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP, /* expectedChangedPrivateFlags */
+                0, /* expectedFlagsValue */
+                PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP /* expectedPrivateFlagsValue */,
+                true, /* internalSystemWindowGranted */
+                true /* manageActivityTasksGranted */);
+    }
+
+
+    private void testRelayoutFlagChanges(boolean firstRelayout, int startFlags,
+            int startPrivateFlags, int newFlags, int newPrivateFlags, int expectedChangedFlags,
+            int expectedChangedPrivateFlags, int expectedFlagsValue,
+            int expectedPrivateFlagsValue) {
+            testRelayoutFlagChanges(firstRelayout, startFlags, startPrivateFlags, newFlags,
+                    newPrivateFlags, expectedChangedFlags, expectedChangedPrivateFlags,
+                    expectedFlagsValue, expectedPrivateFlagsValue,
+                    true /* internalSystemWindowGranted */,
+                    true /* manageActivityTasksGranted */);
+    }
+
     // Helper method to test relayout of a window, either for the initial layout, or a subsequent
     // one, and makes sure that the flags and private flags changes and final values are properly
     // reported to mDwpcHelper.keepActivityOnWindowFlagsChanged.
     private void testRelayoutFlagChanges(boolean firstRelayout, int startFlags,
             int startPrivateFlags, int newFlags, int newPrivateFlags, int expectedChangedFlags,
             int expectedChangedPrivateFlags, int expectedFlagsValue,
-            int expectedPrivateFlagsValue) {
+            int expectedPrivateFlagsValue, boolean internalSystemWindowGranted,
+            boolean manageActivityTasksGranted) {
         final WindowState win = newWindowBuilder("appWin", TYPE_BASE_APPLICATION).build();
         win.mRelayoutCalled = !firstRelayout;
         mWm.mWindowMap.put(win.mClient.asBinder(), win);
         spyOn(mDisplayContent.mDwpcHelper);
         when(mDisplayContent.mDwpcHelper.hasController()).thenReturn(true);
 
+        doReturn(internalSystemWindowGranted ? PackageManager.PERMISSION_GRANTED
+                : PackageManager.PERMISSION_DENIED).when(mWm.mContext).checkPermission(
+                eq(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW), anyInt(), anyInt());
+        doReturn(manageActivityTasksGranted ? PackageManager.PERMISSION_GRANTED
+                : PackageManager.PERMISSION_DENIED).when(mWm.mContext).checkPermission(
+                eq(android.Manifest.permission.MANAGE_ACTIVITY_TASKS), anyInt(), anyInt());
+
         win.mAttrs.flags = startFlags;
         win.mAttrs.privateFlags = startPrivateFlags;
 
@@ -438,6 +556,12 @@
         ArgumentCaptor<Integer> flagsValue = ArgumentCaptor.forClass(Integer.class);
         ArgumentCaptor<Integer> privateFlagsValue = ArgumentCaptor.forClass(Integer.class);
 
+        if (!firstRelayout && expectedChangedFlags == 0 && expectedChangedPrivateFlags == 0) {
+            verify(mDisplayContent.mDwpcHelper, never()).keepActivityOnWindowFlagsChanged(
+                    any(ActivityInfo.class), anyInt(), anyInt(), anyInt(), anyInt());
+            return;
+        }
+
         verify(mDisplayContent.mDwpcHelper).keepActivityOnWindowFlagsChanged(
                 any(ActivityInfo.class), changedFlags.capture(), changedPrivateFlags.capture(),
                 flagsValue.capture(), privateFlagsValue.capture());