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());