/* * Copyright (C) 2016 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; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.WindowStateAnimator.PRESERVED_SURFACE_LAYER; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Binder; import android.platform.test.annotations.Presubmit; import android.util.SparseBooleanArray; import android.view.IRecentsAnimationRunner; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.window.ScreenCapture; import androidx.test.filters.SmallTest; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.function.Function; /** * Tests for the {@link DisplayContent#assignChildLayers(SurfaceControl.Transaction)} method. * * Build/Install/Run: * atest WmTests:ZOrderingTests */ @SmallTest @Presubmit @WindowTestsBase.UseTestDisplay(addAllCommonWindows = true) @RunWith(WindowTestRunner.class) public class ZOrderingTests extends WindowTestsBase { private static class LayerRecordingTransaction extends SurfaceControl.Transaction { // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder // such that we can keep track of the parents of Surfaces as they are constructed. private final HashMap mParentFor = new HashMap<>(); HashMap mLayersForControl = new HashMap<>(); HashMap mRelativeLayersForControl = new HashMap<>(); @Override public SurfaceControl.Transaction setLayer(SurfaceControl sc, int layer) { mRelativeLayersForControl.remove(sc); mLayersForControl.put(sc, layer); return this; } @Override public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc, SurfaceControl relativeTo, int layer) { mRelativeLayersForControl.put(sc, relativeTo); mLayersForControl.put(sc, layer); return this; } private int getLayer(SurfaceControl sc) { return mLayersForControl.getOrDefault(sc, 0); } private SurfaceControl getRelativeLayer(SurfaceControl sc) { return mRelativeLayersForControl.get(sc); } void addParentFor(SurfaceControl child, SurfaceControl parent) { mParentFor.put(child, parent); } SurfaceControl getParentFor(SurfaceControl child) { return mParentFor.get(child); } @Override public void close() { } } private static class HierarchyRecorder extends SurfaceControl.Builder { private LayerRecordingTransaction mTransaction; private SurfaceControl mPendingParent; HierarchyRecorder(SurfaceSession s, LayerRecordingTransaction transaction) { super(s); mTransaction = transaction; } @Override public SurfaceControl.Builder setParent(SurfaceControl sc) { mPendingParent = sc; return super.setParent(sc); } @Override public SurfaceControl build() { final SurfaceControl sc = super.build(); mTransaction.addParentFor(sc, mPendingParent); mPendingParent = null; return sc; } } private static class HierarchyRecordingBuilderFactory implements Function { private LayerRecordingTransaction mTransaction; HierarchyRecordingBuilderFactory(LayerRecordingTransaction transaction) { mTransaction = transaction; } @Override public SurfaceControl.Builder apply(SurfaceSession s) { final LayerRecordingTransaction transaction = mTransaction; return new HierarchyRecorder(s, transaction); } } private LayerRecordingTransaction mTransaction; @Override void beforeCreateTestDisplay() { // We can't use @Before here because it may happen after WindowTestsBase @Before // which is after construction of the DisplayContent, meaning the HierarchyRecorder // would miss construction of the top-level layers. mTransaction = new LayerRecordingTransaction(); mWm.mSurfaceControlFactory = new HierarchyRecordingBuilderFactory(mTransaction); mWm.mTransactionFactory = () -> mTransaction; } @After public void tearDown() { mTransaction.close(); } private static LinkedList getAncestors(LayerRecordingTransaction t, SurfaceControl sc) { LinkedList p = new LinkedList<>(); SurfaceControl current = sc; do { p.addLast(current); SurfaceControl rs = t.getRelativeLayer(current); if (rs != null) { current = rs; } else { current = t.getParentFor(current); } } while (current != null); return p; } private static void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left, SurfaceControl right) { final LinkedList leftParentChain = getAncestors(t, left); final LinkedList rightParentChain = getAncestors(t, right); SurfaceControl leftTop = leftParentChain.peekLast(); SurfaceControl rightTop = rightParentChain.peekLast(); while (leftTop != null && rightTop != null && leftTop == rightTop) { leftParentChain.removeLast(); rightParentChain.removeLast(); leftTop = leftParentChain.peekLast(); rightTop = rightParentChain.peekLast(); } if (rightTop == null) { // right is the parent of left. assertThat(t.getLayer(leftTop)).isGreaterThan(0); } else if (leftTop == null) { // left is the parent of right. assertThat(t.getLayer(rightTop)).isLessThan(0); } else { assertThat(t.getLayer(leftTop)).isGreaterThan(t.getLayer(rightTop)); } } void assertWindowHigher(WindowState left, WindowState right) { assertZOrderGreaterThan(mTransaction, left.getSurfaceControl(), right.getSurfaceControl()); } WindowState createWindow(String name) { return createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, name); } @Test public void testAssignWindowLayers_ForImeWithNoTarget() { mDisplayContent.setImeLayeringTarget(null); mDisplayContent.assignChildLayers(mTransaction); // The Ime has an higher base layer than app windows and lower base layer than system // windows, so it should be above app windows and below system windows if there isn't an IME // target. assertWindowHigher(mImeWindow, mChildAppWindowAbove); assertWindowHigher(mImeWindow, mAppWindow); assertWindowHigher(mNavBarWindow, mImeWindow); assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTarget() { final WindowState imeAppTarget = createWindow("imeAppTarget"); mDisplayContent.setImeLayeringTarget(imeAppTarget); mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows and below system windows if it is targeting an app // window. assertWindowHigher(mImeWindow, imeAppTarget); assertWindowHigher(mImeWindow, mChildAppWindowAbove); assertWindowHigher(mImeWindow, mAppWindow); assertWindowHigher(mNavBarWindow, mImeWindow); assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() { final WindowState imeAppTarget = createWindow("imeAppTarget"); final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget, TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken, "imeAppTargetChildAboveWindow"); final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget, TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken, "imeAppTargetChildBelowWindow"); mDisplayContent.setImeLayeringTarget(imeAppTarget); makeWindowVisible(mImeWindow); mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows except for child windows that are z-ordered above it // and below system windows if it is targeting an app window. assertWindowHigher(mImeWindow, imeAppTarget); assertWindowHigher(imeAppTargetChildAboveWindow, mImeWindow); assertWindowHigher(mImeWindow, mChildAppWindowAbove); assertWindowHigher(mImeWindow, mAppWindow); assertWindowHigher(mNavBarWindow, mImeWindow); assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() { final WindowState appBelowImeTarget = createWindow("appBelowImeTarget"); final WindowState imeAppTarget = createWindow("imeAppTarget"); final WindowState appAboveImeTarget = createWindow("appAboveImeTarget"); mDisplayContent.setImeLayeringTarget(imeAppTarget); mDisplayContent.setImeControlTarget(imeAppTarget); mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows except for non-fullscreen app window above it and // below system windows if it is targeting an app window. assertWindowHigher(mImeWindow, imeAppTarget); assertWindowHigher(mImeWindow, appBelowImeTarget); assertWindowHigher(appAboveImeTarget, mImeWindow); assertWindowHigher(mImeWindow, mChildAppWindowAbove); assertWindowHigher(mImeWindow, mAppWindow); assertWindowHigher(mNavBarWindow, mImeWindow); assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeNonAppImeTarget() { final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY, mDisplayContent, "imeSystemOverlayTarget", true /* ownerCanAddInternalSystemWindow */); mDisplayContent.setImeLayeringTarget(imeSystemOverlayTarget); mDisplayContent.assignChildLayers(mTransaction); // The IME target base layer is higher than all window except for the nav bar window, so the // IME should be above all windows except for the nav bar. assertWindowHigher(mImeWindow, imeSystemOverlayTarget); assertWindowHigher(mImeWindow, mChildAppWindowAbove); assertWindowHigher(mImeWindow, mAppWindow); // The IME has a higher base layer than the status bar so we may expect it to go // above the status bar once they are both in the Non-App layer, as past versions of this // test enforced. However this seems like the wrong behavior unless the status bar is the // IME target. assertWindowHigher(mNavBarWindow, mImeWindow); assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForStatusBarImeTarget() { mDisplayContent.setImeLayeringTarget(mStatusBarWindow); mDisplayContent.setImeControlTarget(mStatusBarWindow); mDisplayContent.assignChildLayers(mTransaction); assertWindowHigher(mImeWindow, mChildAppWindowAbove); assertWindowHigher(mImeWindow, mAppWindow); assertWindowHigher(mImeWindow, mStatusBarWindow); // And, IME dialogs should always have an higher layer than the IME. assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testStackLayers() { final WindowState anyWindow1 = createWindow("anyWindow"); final WindowState pinnedStackWindow = createWindow(null, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow"); final WindowState dockedStackWindow = createWindow(null, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "dockedStackWindow"); final WindowState assistantStackWindow = createWindow(null, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, mDisplayContent, "assistantStackWindow"); final WindowState homeActivityWindow = createWindow(null, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, TYPE_BASE_APPLICATION, mDisplayContent, "homeActivityWindow"); final WindowState anyWindow2 = createWindow("anyWindow2"); mDisplayContent.assignChildLayers(mTransaction); assertWindowHigher(dockedStackWindow, homeActivityWindow); assertWindowHigher(assistantStackWindow, homeActivityWindow); assertWindowHigher(pinnedStackWindow, homeActivityWindow); assertWindowHigher(anyWindow1, homeActivityWindow); assertWindowHigher(anyWindow2, homeActivityWindow); assertWindowHigher(pinnedStackWindow, anyWindow1); assertWindowHigher(pinnedStackWindow, anyWindow2); assertWindowHigher(pinnedStackWindow, dockedStackWindow); assertWindowHigher(pinnedStackWindow, assistantStackWindow); } @Test public void testAssignWindowLayers_ForSysUiPanels() { final WindowState navBarPanel = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, mDisplayContent, "NavBarPanel"); final WindowState statusBarPanel = createWindow(null, TYPE_STATUS_BAR_ADDITIONAL, mDisplayContent, "StatusBarAdditional"); final WindowState statusBarSubPanel = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, mDisplayContent, "StatusBarSubPanel"); mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows and below system windows if it is targeting an app // window. assertWindowHigher(navBarPanel, mNavBarWindow); assertWindowHigher(statusBarPanel, mStatusBarWindow); assertWindowHigher(statusBarSubPanel, statusBarPanel); } @Test public void testAssignWindowLayers_ForImeOnAppWithRecentsAnimating() { final WindowState imeAppTarget = createWindow(null, TYPE_APPLICATION, mAppWindow.mActivityRecord, "imeAppTarget"); mDisplayContent.setImeInputTarget(imeAppTarget); mDisplayContent.setImeLayeringTarget(imeAppTarget); mDisplayContent.setImeControlTarget(imeAppTarget); mDisplayContent.updateImeParent(); // Simulate the ime layering target task is animating with recents animation. final Task imeAppTargetTask = imeAppTarget.getTask(); final SurfaceAnimator imeTargetTaskAnimator = imeAppTargetTask.mSurfaceAnimator; spyOn(imeTargetTaskAnimator); doReturn(ANIMATION_TYPE_RECENTS).when(imeTargetTaskAnimator).getAnimationType(); doReturn(true).when(imeTargetTaskAnimator).isAnimating(); mDisplayContent.assignChildLayers(mTransaction); // Ime should on top of the application window when in recents animation and keep // attached on app. assertTrue(mDisplayContent.shouldImeAttachedToApp()); assertWindowHigher(mImeWindow, imeAppTarget); } @Test public void testAssignWindowLayers_ForImeOnPopupImeLayeringTarget() { final WindowState imeAppTarget = createWindow(null, TYPE_APPLICATION, mAppWindow.mActivityRecord, "imeAppTarget"); mDisplayContent.setImeInputTarget(imeAppTarget); mDisplayContent.setImeLayeringTarget(imeAppTarget); mDisplayContent.setImeControlTarget(imeAppTarget); // Set a popup IME layering target and keeps the original IME control target behinds it. final WindowState popupImeTargetWin = createWindow(imeAppTarget, TYPE_APPLICATION_SUB_PANEL, mAppWindow.mActivityRecord, "popupImeTargetWin"); mDisplayContent.setImeLayeringTarget(popupImeTargetWin); mDisplayContent.updateImeParent(); // Ime should on top of the popup IME layering target window. mDisplayContent.assignChildLayers(mTransaction); assertWindowHigher(mImeWindow, popupImeTargetWin); } @Test public void testAssignWindowLayers_ForNegativelyZOrderedSubtype() { // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA // then we can drop all negative layering on the windowing side. final WindowState anyWindow = createWindow("anyWindow"); final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent, "TypeApplicationMediaChild"); final WindowState mediaOverlayChild = createWindow(anyWindow, TYPE_APPLICATION_MEDIA_OVERLAY, mDisplayContent, "TypeApplicationMediaOverlayChild"); mDisplayContent.assignChildLayers(mTransaction); assertWindowHigher(anyWindow, mediaOverlayChild); assertWindowHigher(mediaOverlayChild, child); } @Test public void testAssignWindowLayers_ForPostivelyZOrderedSubtype() { final WindowState anyWindow = createWindow("anyWindow"); final ArrayList childList = new ArrayList<>(); childList.add(createWindow(anyWindow, TYPE_APPLICATION_PANEL, mDisplayContent, "TypeApplicationPanelChild")); childList.add(createWindow(anyWindow, TYPE_APPLICATION_SUB_PANEL, mDisplayContent, "TypeApplicationSubPanelChild")); childList.add(createWindow(anyWindow, TYPE_APPLICATION_ATTACHED_DIALOG, mDisplayContent, "TypeApplicationAttachedDialogChild")); childList.add(createWindow(anyWindow, TYPE_APPLICATION_ABOVE_SUB_PANEL, mDisplayContent, "TypeApplicationAboveSubPanelPanelChild")); final LayerRecordingTransaction t = mTransaction; mDisplayContent.assignChildLayers(t); for (int i = childList.size() - 1; i >= 0; i--) { assertThat(t.getLayer(childList.get(i).getSurfaceControl())) .isGreaterThan(PRESERVED_SURFACE_LAYER); } } @Test public void testAttachNavBarWhenEnteringRecents_expectNavBarHigherThanIme() { // create RecentsAnimationController IRecentsAnimationRunner mockRunner = mock(IRecentsAnimationRunner.class); when(mockRunner.asBinder()).thenReturn(new Binder()); final int displayId = mDisplayContent.getDisplayId(); RecentsAnimationController controller = new RecentsAnimationController( mWm, mockRunner, null, displayId); spyOn(controller); doReturn(mNavBarWindow).when(controller).getNavigationBarWindow(); mWm.setRecentsAnimationController(controller); // set ime visible spyOn(mDisplayContent.mInputMethodWindow); doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible(); DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); spyOn(policy); doReturn(true).when(policy).shouldAttachNavBarToAppDuringTransition(); // create home activity Task rootHomeTask = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask(); final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService) .setParentTask(rootHomeTask) .setCreateTask(true) .build(); homeActivity.setVisibility(true); // start recent animation controller.initialize(homeActivity.getActivityType(), new SparseBooleanArray(), homeActivity); mDisplayContent.assignChildLayers(mTransaction); assertZOrderGreaterThan(mTransaction, mNavBarWindow.mToken.getSurfaceControl(), mDisplayContent.getImeContainer().getSurfaceControl()); } @Test public void testPopupWindowAndParentIsImeTarget_expectHigherThanIme_inMultiWindow() { // Simulate the app window is in multi windowing mode and being IME target mAppWindow.getConfiguration().windowConfiguration.setWindowingMode( WINDOWING_MODE_MULTI_WINDOW); mDisplayContent.setImeLayeringTarget(mAppWindow); mDisplayContent.setImeInputTarget(mAppWindow); makeWindowVisible(mImeWindow); // Create a popupWindow assertWindowHigher(mImeWindow, mAppWindow); final WindowState popupWindow = createWindow(mAppWindow, TYPE_APPLICATION_PANEL, mDisplayContent, "PopupWindow"); spyOn(popupWindow); mDisplayContent.assignChildLayers(mTransaction); // Verify the surface layer of the popupWindow should higher than IME verify(popupWindow).needsRelativeLayeringToIme(); assertThat(popupWindow.needsRelativeLayeringToIme()).isTrue(); assertZOrderGreaterThan(mTransaction, popupWindow.getSurfaceControl(), mDisplayContent.getImeContainer().getSurfaceControl()); } @Test public void testSystemDialogWindow_expectHigherThanIme_inMultiWindow() { // Simulate the app window is in multi windowing mode and being IME target mAppWindow.getConfiguration().windowConfiguration.setWindowingMode( WINDOWING_MODE_MULTI_WINDOW); mDisplayContent.setImeLayeringTarget(mAppWindow); mDisplayContent.setImeInputTarget(mAppWindow); makeWindowVisible(mImeWindow); // Create a popupWindow final WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY, mDisplayContent, "SystemDialog", true); systemDialogWindow.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; spyOn(systemDialogWindow); mDisplayContent.assignChildLayers(mTransaction); // Verify the surface layer of the popupWindow should higher than IME verify(systemDialogWindow).needsRelativeLayeringToIme(); assertThat(systemDialogWindow.needsRelativeLayeringToIme()).isTrue(); assertZOrderGreaterThan(mTransaction, systemDialogWindow.getSurfaceControl(), mDisplayContent.getImeContainer().getSurfaceControl()); } @Test public void testImeScreenshotLayer() { final Task task = createTask(mDisplayContent); final WindowState imeAppTarget = createAppWindow(task, TYPE_APPLICATION, "imeAppTarget"); final Rect bounds = mImeWindow.getParentFrame(); final ScreenCapture.ScreenshotHardwareBuffer imeBuffer = ScreenCapture.captureLayersExcluding(mImeWindow.getSurfaceControl(), bounds, 1.0f, PixelFormat.RGB_565, null); spyOn(mDisplayContent.mWmService.mTaskSnapshotController); doReturn(imeBuffer).when(mDisplayContent.mWmService.mTaskSnapshotController) .snapshotImeFromAttachedTask(task); mDisplayContent.showImeScreenshot(imeAppTarget); assertEquals(imeAppTarget, mDisplayContent.mImeScreenshot.getImeTarget()); assertNotNull(mDisplayContent.mImeScreenshot); assertZOrderGreaterThan(mTransaction, mDisplayContent.mImeScreenshot.getImeScreenshotSurface(), imeAppTarget.mSurfaceControl); } }