From d5c7dd6da810a6b89151b337bea79fd817e6b72a Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Wed, 8 Mar 2017 10:39:30 -0800 Subject: [PATCH] Modify SurfaceView to use SurfaceFlinger child surfaces. Here we have SurfaceView bypass the WindowManager and speak directly to SurfaceFlinger using child surfaces. We also implement some logic in the WM to handle child surfaces in various Surface replacement scenarios. For those following along in the revert Saga, this also includes the follow up CLs to the original CL. - Surface inset calculation - Animation fixes. The error causing the revert was an incorrect JNI signature around deferTransactionUntilSurface. I've noted it inline. Bug: 28858420 Bug: 31518219 Bug: 34888808 Bug: 35588318 Bug: 35396882 Test: Existing tests still pass (except for the ones that don't and will be deleted). Change-Id: Ie56b6f7ab16f32d7fc459b8eba26594337ad55de --- core/java/android/view/SurfaceControl.java | 23 +- core/java/android/view/SurfaceSession.java | 5 + core/java/android/view/SurfaceView.java | 455 +++++++----------- core/java/android/view/ViewRootImpl.java | 10 +- .../internal/view/SurfaceCallbackHelper.java | 21 +- core/jni/android_view_RenderNode.cpp | 8 +- core/jni/android_view_SurfaceControl.cpp | 28 +- core/jni/android_view_SurfaceSession.cpp | 11 +- core/jni/android_view_ThreadedRenderer.cpp | 4 - libs/hwui/TreeInfo.h | 2 - media/java/android/media/tv/TvView.java | 4 +- .../com/android/server/wm/AppWindowToken.java | 10 + .../server/wm/WindowManagerService.java | 9 + .../com/android/server/wm/WindowState.java | 7 + .../server/wm/WindowStateAnimator.java | 20 + .../server/wm/WindowSurfaceController.java | 14 + 16 files changed, 328 insertions(+), 303 deletions(-) diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index b718696b22021..519c1e2454df6 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -95,6 +95,11 @@ public class SurfaceControl { IBinder displayToken, int mode); private static native void nativeDeferTransactionUntil(long nativeObject, IBinder handle, long frame); + private static native void nativeDeferTransactionUntilSurface(long nativeObject, + long surfaceObject, long frame); + private static native void nativeReparentChildren(long nativeObject, + IBinder handle); + private static native void nativeSeverChildren(long nativeObject); private static native void nativeSetOverrideScalingMode(long nativeObject, int scalingMode); private static native IBinder nativeGetHandle(long nativeObject); @@ -418,7 +423,23 @@ public class SurfaceControl { } public void deferTransactionUntil(IBinder handle, long frame) { - nativeDeferTransactionUntil(mNativeObject, handle, frame); + if (frame > 0) { + nativeDeferTransactionUntil(mNativeObject, handle, frame); + } + } + + public void deferTransactionUntil(Surface barrier, long frame) { + if (frame > 0) { + nativeDeferTransactionUntilSurface(mNativeObject, barrier.mNativeObject, frame); + } + } + + public void reparentChildren(IBinder newParentHandle) { + nativeReparentChildren(mNativeObject, newParentHandle); + } + + public void detachChildren() { + nativeSeverChildren(mNativeObject); } public void setOverrideScalingMode(int scalingMode) { diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java index 3cf5af4846259..b5912bc1e1c89 100644 --- a/core/java/android/view/SurfaceSession.java +++ b/core/java/android/view/SurfaceSession.java @@ -27,6 +27,7 @@ public final class SurfaceSession { private long mNativeClient; // SurfaceComposerClient* private static native long nativeCreate(); + private static native long nativeCreateScoped(long surfacePtr); private static native void nativeDestroy(long ptr); private static native void nativeKill(long ptr); @@ -35,6 +36,10 @@ public final class SurfaceSession { mNativeClient = nativeCreate(); } + public SurfaceSession(Surface root) { + mNativeClient = nativeCreateScoped(root.mNativeObject); + } + /* no user serviceable parts here ... */ @Override protected void finalize() throws Throwable { diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index d2577d48c3d18..61b12475d5427 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -16,6 +16,10 @@ package android.view; +import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_SUBLAYER; +import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_OVERLAY_SUBLAYER; +import static android.view.WindowManagerPolicy.APPLICATION_PANEL_SUBLAYER; + import android.content.Context; import android.content.res.CompatibilityInfo.Translator; import android.content.res.Configuration; @@ -26,16 +30,12 @@ import android.graphics.Rect; import android.graphics.Region; import android.os.Handler; import android.os.Message; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; -import com.android.internal.view.BaseIWindow; import com.android.internal.view.SurfaceCallbackHelper; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.concurrent.locks.ReentrantLock; @@ -92,8 +92,8 @@ import java.util.concurrent.locks.ReentrantLock; * positioned asynchronously.

*/ public class SurfaceView extends View { - static private final String TAG = "SurfaceView"; - static private final boolean DEBUG = false; + private static final String TAG = "SurfaceView"; + private static final boolean DEBUG = false; final ArrayList mCallbacks = new ArrayList(); @@ -102,28 +102,23 @@ public class SurfaceView extends View { final ReentrantLock mSurfaceLock = new ReentrantLock(); final Surface mSurface = new Surface(); // Current surface in use - final Surface mNewSurface = new Surface(); // New surface we are switching to boolean mDrawingStopped = true; + // We use this to track if the application has produced a frame + // in to the Surface. Up until that point, we should be careful not to punch + // holes. + boolean mDrawFinished = false; - final WindowManager.LayoutParams mLayout - = new WindowManager.LayoutParams(); - IWindowSession mSession; - MyWindow mWindow; - final Rect mVisibleInsets = new Rect(); - final Rect mWinFrame = new Rect(); - final Rect mOverscanInsets = new Rect(); - final Rect mContentInsets = new Rect(); - final Rect mStableInsets = new Rect(); - final Rect mOutsets = new Rect(); - final Rect mBackdropFrame = new Rect(); + final Rect mScreenRect = new Rect(); + SurfaceSession mSurfaceSession; + + SurfaceControl mSurfaceControl; final Rect mTmpRect = new Rect(); final Configuration mConfiguration = new Configuration(); static final int KEEP_SCREEN_ON_MSG = 1; - static final int GET_NEW_SURFACE_MSG = 2; - static final int UPDATE_WINDOW_MSG = 3; + static final int DRAW_FINISHED_MSG = 2; - int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; + int mSubLayer = APPLICATION_MEDIA_SUBLAYER; boolean mIsCreating = false; private volatile boolean mRtHandlingPositionUpdates = false; @@ -135,11 +130,9 @@ public class SurfaceView extends View { case KEEP_SCREEN_ON_MSG: { setKeepScreenOn(msg.arg1 != 0); } break; - case GET_NEW_SURFACE_MSG: { - handleGetNewSurface(); - } break; - case UPDATE_WINDOW_MSG: { - updateWindow(); + case DRAW_FINISHED_MSG: { + mDrawFinished = true; + invalidate(); } break; } } @@ -149,7 +142,7 @@ public class SurfaceView extends View { = new ViewTreeObserver.OnScrollChangedListener() { @Override public void onScrollChanged() { - updateWindow(); + updateSurface(); } }; @@ -159,13 +152,14 @@ public class SurfaceView extends View { public boolean onPreDraw() { // reposition ourselves where the surface is mHaveFrame = getWidth() > 0 && getHeight() > 0; - updateWindow(); + updateSurface(); return true; } }; boolean mRequestedVisible = false; boolean mWindowVisibility = false; + boolean mLastWindowVisibility = false; boolean mViewVisibility = false; int mRequestedWidth = -1; int mRequestedHeight = -1; @@ -181,19 +175,17 @@ public class SurfaceView extends View { boolean mVisible = false; int mWindowSpaceLeft = -1; int mWindowSpaceTop = -1; - int mWindowSpaceWidth = -1; - int mWindowSpaceHeight = -1; + int mSurfaceWidth = -1; + int mSurfaceHeight = -1; int mFormat = -1; final Rect mSurfaceFrame = new Rect(); int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1; - boolean mUpdateWindowNeeded; - boolean mReportDrawNeeded; private Translator mTranslator; - private int mWindowInsetLeft; - private int mWindowInsetTop; private boolean mGlobalListenersAdded; + private int mSurfaceFlags = SurfaceControl.HIDDEN; + public SurfaceView(Context context) { this(context, null); } @@ -227,11 +219,8 @@ public class SurfaceView extends View { protected void onAttachedToWindow() { super.onAttachedToWindow(); mParent.requestTransparentRegion(this); - mSession = getWindowSession(); - mLayout.token = getWindowToken(); - mLayout.setTitle("SurfaceView - " + getViewRootImpl().getTitle()); - mLayout.packageName = mContext.getOpPackageName(); mViewVisibility = getVisibility() == VISIBLE; + mRequestedVisible = mViewVisibility && mWindowVisibility; if (!mGlobalListenersAdded) { ViewTreeObserver observer = getViewTreeObserver(); @@ -246,7 +235,7 @@ public class SurfaceView extends View { super.onWindowVisibilityChanged(visibility); mWindowVisibility = visibility == VISIBLE; mRequestedVisible = mWindowVisibility && mViewVisibility; - updateWindow(); + updateSurface(); } @Override @@ -264,7 +253,7 @@ public class SurfaceView extends View { requestLayout(); } mRequestedVisible = newRequestedVisible; - updateWindow(); + updateSurface(); } @Override @@ -277,19 +266,14 @@ public class SurfaceView extends View { } mRequestedVisible = false; - updateWindow(); - mHaveFrame = false; - if (mWindow != null) { - try { - mSession.remove(mWindow); - } catch (RemoteException ex) { - // Not much we can do here... - } - mWindow = null; - } - mSession = null; - mLayout.token = null; + updateSurface(); + if (mSurfaceControl != null) { + mSurfaceControl.destroy(); + } + mSurfaceControl = null; + + mHaveFrame = false; super.onDetachedFromWindow(); } @@ -308,13 +292,13 @@ public class SurfaceView extends View { @Override protected boolean setFrame(int left, int top, int right, int bottom) { boolean result = super.setFrame(left, top, right, bottom); - updateWindow(); + updateSurface(); return result; } @Override public boolean gatherTransparentRegion(Region region) { - if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { + if (isAboveParent()) { return super.gatherTransparentRegion(region); } @@ -341,7 +325,7 @@ public class SurfaceView extends View { @Override public void draw(Canvas canvas) { - if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { + if (mDrawFinished && !isAboveParent()) { // draw() is not called when SKIP_DRAW is set if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) { // punch a whole in the view-hierarchy below us @@ -353,8 +337,8 @@ public class SurfaceView extends View { @Override protected void dispatchDraw(Canvas canvas) { - if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { - // if SKIP_DRAW is cleared, draw() has already punched a hole + if (mDrawFinished && !isAboveParent()) { + // draw() is not called when SKIP_DRAW is set if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { // punch a whole in the view-hierarchy below us canvas.drawColor(0, PorterDuff.Mode.CLEAR); @@ -375,9 +359,8 @@ public class SurfaceView extends View { *

Calling this overrides any previous call to {@link #setZOrderOnTop}. */ public void setZOrderMediaOverlay(boolean isMediaOverlay) { - mWindowType = isMediaOverlay - ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY - : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; + mSubLayer = isMediaOverlay + ? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER; } /** @@ -395,12 +378,9 @@ public class SurfaceView extends View { */ public void setZOrderOnTop(boolean onTop) { if (onTop) { - mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; - // ensures the surface is placed below the IME - mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + mSubLayer = APPLICATION_PANEL_SUBLAYER; } else { - mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; - mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + mSubLayer = APPLICATION_MEDIA_SUBLAYER; } } @@ -418,31 +398,32 @@ public class SurfaceView extends View { */ public void setSecure(boolean isSecure) { if (isSecure) { - mLayout.flags |= WindowManager.LayoutParams.FLAG_SECURE; + mSurfaceFlags |= SurfaceControl.SECURE; } else { - mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SECURE; + mSurfaceFlags &= ~SurfaceControl.SECURE; } } - /** - * Hack to allow special layering of windows. The type is one of the - * types in WindowManager.LayoutParams. This is a hack so: - * @hide - */ - public void setWindowType(int type) { - mWindowType = type; + private Rect getParentSurfaceInsets() { + final ViewRootImpl root = getViewRootImpl(); + if (root == null) { + return null; + } else { + return root.mWindowAttributes.surfaceInsets; + } } /** @hide */ - protected void updateWindow() { + protected void updateSurface() { if (!mHaveFrame) { return; } ViewRootImpl viewRoot = getViewRootImpl(); - if (viewRoot != null) { - mTranslator = viewRoot.mTranslator; + if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) { + return; } + mTranslator = viewRoot.mTranslator; if (mTranslator != null) { mSurface.setCompatibilityTranslator(mTranslator); } @@ -452,17 +433,15 @@ public class SurfaceView extends View { int myHeight = mRequestedHeight; if (myHeight <= 0) myHeight = getHeight(); - final boolean creating = mWindow == null; final boolean formatChanged = mFormat != mRequestedFormat; - final boolean sizeChanged = mWindowSpaceWidth != myWidth || mWindowSpaceHeight != myHeight; + final boolean creating = (mSurfaceControl == null || formatChanged) + && mRequestedVisible; + final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight; final boolean visibleChanged = mVisible != mRequestedVisible; - final boolean layoutSizeChanged = getWidth() != mLayout.width - || getHeight() != mLayout.height; - + final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility; boolean redrawNeeded = false; - if (creating || formatChanged || sizeChanged || visibleChanged - || mUpdateWindowNeeded || mReportDrawNeeded) { + if (creating || formatChanged || sizeChanged || visibleChanged || windowVisibleChanged) { getLocationInWindow(mLocation); if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " @@ -476,93 +455,77 @@ public class SurfaceView extends View { final boolean visible = mVisible = mRequestedVisible; mWindowSpaceLeft = mLocation[0]; mWindowSpaceTop = mLocation[1]; - mWindowSpaceWidth = myWidth; - mWindowSpaceHeight = myHeight; + mSurfaceWidth = myWidth; + mSurfaceHeight = myHeight; mFormat = mRequestedFormat; + mLastWindowVisibility = mWindowVisibility; - // Scaling/Translate window's layout here because mLayout is not used elsewhere. - - // Places the window relative - mLayout.x = mWindowSpaceLeft; - mLayout.y = mWindowSpaceTop; - mLayout.width = getWidth(); - mLayout.height = getHeight(); + mScreenRect.left = mWindowSpaceLeft; + mScreenRect.top = mWindowSpaceTop; + mScreenRect.right = mWindowSpaceLeft + getWidth(); + mScreenRect.bottom = mWindowSpaceTop + getHeight(); if (mTranslator != null) { - mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout); + mTranslator.translateRectInAppWindowToScreen(mScreenRect); } - mLayout.format = mRequestedFormat; - mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS - | WindowManager.LayoutParams.FLAG_SCALED - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE - ; - if (!creating && !sizeChanged) { - mLayout.privateFlags |= - WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY; - } else { - mLayout.privateFlags &= - ~WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY; + final Rect surfaceInsets = getParentSurfaceInsets(); + mScreenRect.offset(surfaceInsets.left, surfaceInsets.top); + + if (creating) { + mSurfaceSession = new SurfaceSession(viewRoot.mSurface); + mSurfaceControl = new SurfaceControl(mSurfaceSession, + "SurfaceView - " + viewRoot.getTitle().toString(), + mSurfaceWidth, mSurfaceHeight, mFormat, + mSurfaceFlags); } - if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) { - mLayout.privateFlags |= - WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; - } - mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION - | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; - - if (mWindow == null) { - Display display = getDisplay(); - mWindow = new MyWindow(this); - mLayout.type = mWindowType; - mLayout.gravity = Gravity.START|Gravity.TOP; - mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout, - mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets, - mStableInsets); - } - - boolean realSizeChanged; - boolean reportDrawNeeded; - - int relayoutResult; + boolean realSizeChanged = false; mSurfaceLock.lock(); try { - mUpdateWindowNeeded = false; - reportDrawNeeded = mReportDrawNeeded; - mReportDrawNeeded = false; mDrawingStopped = !visible; if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Cur surface: " + mSurface); - relayoutResult = mSession.relayout( - mWindow, mWindow.mSeq, mLayout, mWindowSpaceWidth, mWindowSpaceHeight, - visible ? VISIBLE : GONE, - WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY, - mWinFrame, mOverscanInsets, mContentInsets, - mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame, - mConfiguration, mNewSurface); - if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { - reportDrawNeeded = true; + SurfaceControl.openTransaction(); + try { + mSurfaceControl.setLayer(mSubLayer); + if (mViewVisibility) { + mSurfaceControl.show(); + } else { + mSurfaceControl.hide(); + } + + // While creating the surface, we will set it's initial + // geometry. Outside of that though, we should generally + // leave it to the RenderThread. + if (creating || !mRtHandlingPositionUpdates) { + mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top); + mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth, + 0.0f, 0.0f, + mScreenRect.height() / (float) mSurfaceHeight); + } + if (sizeChanged) { + mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight); + } + } finally { + SurfaceControl.closeTransaction(); } - if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " - + "New surface: " + mNewSurface - + ", vis=" + visible + ", frame=" + mWinFrame); + if (sizeChanged || creating) { + redrawNeeded = true; + } mSurfaceFrame.left = 0; mSurfaceFrame.top = 0; if (mTranslator == null) { - mSurfaceFrame.right = mWinFrame.width(); - mSurfaceFrame.bottom = mWinFrame.height(); + mSurfaceFrame.right = mSurfaceWidth; + mSurfaceFrame.bottom = mSurfaceHeight; } else { float appInvertedScale = mTranslator.applicationInvertedScale; - mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f); - mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f); + mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f); + mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f); } final int surfaceWidth = mSurfaceFrame.right; @@ -576,12 +539,11 @@ public class SurfaceView extends View { } try { - redrawNeeded |= creating | reportDrawNeeded; + redrawNeeded |= visible && !mDrawFinished; SurfaceHolder.Callback callbacks[] = null; - final boolean surfaceChanged = (relayoutResult - & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0; + final boolean surfaceChanged = creating; if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) { mSurfaceCreated = false; if (mSurface.isValid()) { @@ -608,7 +570,10 @@ public class SurfaceView extends View { } } - mSurface.transferFrom(mNewSurface); + if (creating) { + mSurface.copyFrom(mSurfaceControl); + } + if (visible && mSurface.isValid()) { if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) { mSurfaceCreated = true; @@ -641,53 +606,55 @@ public class SurfaceView extends View { callbacks = getSurfaceCallbacks(); } SurfaceCallbackHelper sch = - new SurfaceCallbackHelper(mSession, mWindow); + new SurfaceCallbackHelper(this::onDrawFinished); sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); } } } finally { mIsCreating = false; - mSession.performDeferredDestroy(mWindow); + if (mSurfaceControl != null && !mSurfaceCreated) { + mSurfaceControl.destroy(); + mSurfaceControl = null; + } } - } catch (RemoteException ex) { + } catch (Exception ex) { Log.e(TAG, "Exception from relayout", ex); } if (DEBUG) Log.v( - TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + - " w=" + mLayout.width + " h=" + mLayout.height + - ", frame=" + mSurfaceFrame); + TAG, "Layout: x=" + mScreenRect.left + " y=" + mScreenRect.top + + " w=" + mScreenRect.width() + " h=" + mScreenRect.height() + + ", frame=" + mSurfaceFrame); } else { // Calculate the window position in case RT loses the window // and we need to fallback to a UI-thread driven position update - getLocationInWindow(mLocation); + getLocationInSurface(mLocation); final boolean positionChanged = mWindowSpaceLeft != mLocation[0] || mWindowSpaceTop != mLocation[1]; + final boolean layoutSizeChanged = getWidth() != mScreenRect.width() + || getHeight() != mScreenRect.height(); if (positionChanged || layoutSizeChanged) { // Only the position has changed mWindowSpaceLeft = mLocation[0]; mWindowSpaceTop = mLocation[1]; - // For our size changed check, we keep mLayout.width and mLayout.height + // For our size changed check, we keep mScreenRect.width() and mScreenRect.height() // in view local space. - mLocation[0] = mLayout.width = getWidth(); - mLocation[1] = mLayout.height = getHeight(); + mLocation[0] = getWidth(); + mLocation[1] = getHeight(); - transformFromViewToWindowSpace(mLocation); - - mTmpRect.set(mWindowSpaceLeft, mWindowSpaceTop, + mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop, mLocation[0], mLocation[1]); if (mTranslator != null) { - mTranslator.translateRectInAppWindowToScreen(mTmpRect); + mTranslator.translateRectInAppWindowToScreen(mScreenRect); } if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) { try { - if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition UI, " + + if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition UI, " + "postion = [%d, %d, %d, %d]", System.identityHashCode(this), - mTmpRect.left, mTmpRect.top, - mTmpRect.right, mTmpRect.bottom)); - mSession.repositionChild(mWindow, mTmpRect.left, mTmpRect.top, - mTmpRect.right, mTmpRect.bottom, -1, mTmpRect); - } catch (RemoteException ex) { + mScreenRect.left, mScreenRect.top, + mScreenRect.right, mScreenRect.bottom)); + setParentSpaceRectangle(mScreenRect, -1); + } catch (Exception ex) { Log.e(TAG, "Exception from relayout", ex); } } @@ -695,20 +662,43 @@ public class SurfaceView extends View { } } + private void onDrawFinished() { + if (DEBUG) { + Log.i(TAG, System.identityHashCode(this) + " " + + "finishedDrawing"); + } + mHandler.sendEmptyMessage(DRAW_FINISHED_MSG); + } + + private void setParentSpaceRectangle(Rect position, long frameNumber) { + ViewRootImpl viewRoot = getViewRootImpl(); + + SurfaceControl.openTransaction(); + try { + if (frameNumber > 0) { + mSurfaceControl.deferTransactionUntil(viewRoot.mSurface, frameNumber); + } + mSurfaceControl.setPosition(position.left, position.top); + mSurfaceControl.setMatrix(position.width() / (float) mSurfaceWidth, + 0.0f, 0.0f, + position.height() / (float) mSurfaceHeight); + } finally { + SurfaceControl.closeTransaction(); + } + } + private Rect mRTLastReportedPosition = new Rect(); /** * Called by native by a Rendering Worker thread to update the window position * @hide */ - public final void updateWindowPosition_renderWorker(long frameNumber, + public final void updateSurfacePosition_renderWorker(long frameNumber, int left, int top, int right, int bottom) { - IWindowSession session = mSession; - MyWindow window = mWindow; - if (session == null || window == null) { - // Guess we got detached, that sucks + if (mSurfaceControl == null) { return; } + // TODO: This is teensy bit racey in that a brand new SurfaceView moving on // its 2nd frame if RenderThread is running slowly could potentially see // this as false, enter the branch, get pre-empted, then this comes along @@ -726,35 +716,29 @@ public class SurfaceView extends View { } try { if (DEBUG) { - Log.d(TAG, String.format("%d updateWindowPosition RenderWorker, frameNr = %d, " + + Log.d(TAG, String.format("%d updateSurfacePosition RenderWorker, frameNr = %d, " + "postion = [%d, %d, %d, %d]", System.identityHashCode(this), frameNumber, left, top, right, bottom)); } - // Just using mRTLastReportedPosition as a dummy rect here - session.repositionChild(window, left, top, right, bottom, - frameNumber, - mRTLastReportedPosition); - // Now overwrite mRTLastReportedPosition with our values mRTLastReportedPosition.set(left, top, right, bottom); - } catch (RemoteException ex) { + setParentSpaceRectangle(mRTLastReportedPosition, frameNumber); + // Now overwrite mRTLastReportedPosition with our values + } catch (Exception ex) { Log.e(TAG, "Exception from repositionChild", ex); } } /** - * Called by native on RenderThread to notify that the window is no longer in the + * Called by native on RenderThread to notify that the view is no longer in the * draw tree. UI thread is blocked at this point. * @hide */ - public final void windowPositionLost_uiRtSync(long frameNumber) { + public final void surfacePositionLost_uiRtSync(long frameNumber) { if (DEBUG) { Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d", System.identityHashCode(this), frameNumber)); } - IWindowSession session = mSession; - MyWindow window = mWindow; - if (session == null || window == null) { - // We got detached prior to receiving this, abort + if (mSurfaceControl == null) { return; } if (mRtHandlingPositionUpdates) { @@ -763,19 +747,14 @@ public class SurfaceView extends View { // safely access other member variables at this time. // So do what the UI thread would have done if RT wasn't handling position // updates. - mTmpRect.set(mLayout.x, mLayout.y, - mLayout.x + mLayout.width, - mLayout.y + mLayout.height); - - if (!mTmpRect.isEmpty() && !mTmpRect.equals(mRTLastReportedPosition)) { + if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) { try { - if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition, " + + if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition, " + "postion = [%d, %d, %d, %d]", System.identityHashCode(this), - mTmpRect.left, mTmpRect.top, - mTmpRect.right, mTmpRect.bottom)); - session.repositionChild(window, mTmpRect.left, mTmpRect.top, - mTmpRect.right, mTmpRect.bottom, frameNumber, mWinFrame); - } catch (RemoteException ex) { + mScreenRect.left, mScreenRect.top, + mScreenRect.right, mScreenRect.bottom)); + setParentSpaceRectangle(mScreenRect, frameNumber); + } catch (Exception ex) { Log.e(TAG, "Exception from relayout", ex); } } @@ -792,10 +771,6 @@ public class SurfaceView extends View { return callbacks; } - void handleGetNewSurface() { - updateWindow(); - } - /** * Check to see if the surface has fixed size dimensions or if the surface's * dimensions are dimensions are dependent on its current layout. @@ -807,65 +782,8 @@ public class SurfaceView extends View { return (mRequestedWidth != -1 || mRequestedHeight != -1); } - private static class MyWindow extends BaseIWindow { - private final WeakReference mSurfaceView; - - public MyWindow(SurfaceView surfaceView) { - mSurfaceView = new WeakReference(surfaceView); - } - - @Override - public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, - Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, - Configuration newConfig, Rect backDropRect, boolean forceLayout, - boolean alwaysConsumeNavBar, int displayId) { - SurfaceView surfaceView = mSurfaceView.get(); - if (surfaceView != null) { - if (DEBUG) Log.v(TAG, surfaceView + " got resized: w=" + frame.width() - + " h=" + frame.height() + ", cur w=" + mCurWidth + " h=" + mCurHeight); - surfaceView.mSurfaceLock.lock(); - try { - if (reportDraw) { - surfaceView.mUpdateWindowNeeded = true; - surfaceView.mReportDrawNeeded = true; - surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG); - } else if (surfaceView.mWinFrame.width() != frame.width() - || surfaceView.mWinFrame.height() != frame.height() - || forceLayout) { - surfaceView.mUpdateWindowNeeded = true; - surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG); - } - } finally { - surfaceView.mSurfaceLock.unlock(); - } - } - } - - @Override - public void dispatchAppVisibility(boolean visible) { - // The point of SurfaceView is to let the app control the surface. - } - - @Override - public void dispatchGetNewSurface() { - SurfaceView surfaceView = mSurfaceView.get(); - if (surfaceView != null) { - Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG); - surfaceView.mHandler.sendMessage(msg); - } - } - - @Override - public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) { - Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled); - } - - @Override - public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { - } - - int mCurWidth = -1; - int mCurHeight = -1; + private boolean isAboveParent() { + return mSubLayer >= 0; } private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() { @@ -913,15 +831,14 @@ public class SurfaceView extends View { @Override public void setFormat(int format) { - // for backward compatibility reason, OPAQUE always // means 565 for SurfaceView if (format == PixelFormat.OPAQUE) format = PixelFormat.RGB_565; mRequestedFormat = format; - if (mWindow != null) { - updateWindow(); + if (mSurfaceControl != null) { + updateSurface(); } } @@ -982,10 +899,10 @@ public class SurfaceView extends View { mSurfaceLock.lock(); if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped=" - + mDrawingStopped + ", win=" + mWindow); + + mDrawingStopped + ", surfaceControl=" + mSurfaceControl); Canvas c = null; - if (!mDrawingStopped && mWindow != null) { + if (!mDrawingStopped && mSurfaceControl != null) { try { if (hardware) { c = mSurface.lockHardwareCanvas(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 20d960fff6611..f9863b0a67615 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2632,6 +2632,14 @@ public final class ViewRootImpl implements ViewParent, } } + private void onDrawFinished() { + try { + mWindowSession.finishDrawing(mWindow); + } catch (RemoteException e) { + // Have fun! + } + } + private void performDraw() { if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) { return; @@ -2682,7 +2690,7 @@ public final class ViewRootImpl implements ViewParent, } if (mSurfaceHolder != null && mSurface.isValid()) { - SurfaceCallbackHelper sch = new SurfaceCallbackHelper(mWindowSession, mWindow); + SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::onDrawFinished); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); diff --git a/core/java/com/android/internal/view/SurfaceCallbackHelper.java b/core/java/com/android/internal/view/SurfaceCallbackHelper.java index 5b6a82cf1c434..507b673ec2792 100644 --- a/core/java/com/android/internal/view/SurfaceCallbackHelper.java +++ b/core/java/com/android/internal/view/SurfaceCallbackHelper.java @@ -17,14 +17,11 @@ package com.android.internal.view; import android.os.RemoteException; -import android.view.IWindow; -import android.view.IWindowSession; import android.view.Surface; import android.view.SurfaceHolder; public class SurfaceCallbackHelper { - IWindowSession mSession; - IWindow.Stub mWindow; + Runnable mRunnable; int mFinishDrawingCollected = 0; int mFinishDrawingExpected = 0; @@ -37,26 +34,18 @@ public class SurfaceCallbackHelper { if (mFinishDrawingCollected < mFinishDrawingExpected) { return; } - try { - mSession.finishDrawing(mWindow); - } catch (RemoteException e) { - } + mRunnable.run(); } } }; - public SurfaceCallbackHelper(IWindowSession session, - IWindow.Stub window) { - mSession = session; - mWindow = window; + public SurfaceCallbackHelper(Runnable callbacksCollected) { + mRunnable = callbacksCollected; } public void dispatchSurfaceRedrawNeededAsync(SurfaceHolder holder, SurfaceHolder.Callback callbacks[]) { if (callbacks == null || callbacks.length == 0) { - try { - mSession.finishDrawing(mWindow); - } catch (RemoteException e) { - } + mRunnable.run(); return; } diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index f221392f16bdd..6e8c931325626 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -452,10 +452,6 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, const RenderProperties& props = node.properties(); uirenderer::Rect bounds(props.getWidth(), props.getHeight()); transform.mapRect(bounds); - bounds.left -= info.windowInsetLeft; - bounds.right -= info.windowInsetLeft; - bounds.top -= info.windowInsetTop; - bounds.bottom -= info.windowInsetTop; if (CC_LIKELY(transform.isPureTranslate())) { // snap/round the computed bounds, so they match the rounding behavior @@ -627,9 +623,9 @@ static const JNINativeMethod gMethods[] = { int register_android_view_RenderNode(JNIEnv* env) { jclass clazz = FindClassOrDie(env, "android/view/SurfaceView"); gSurfaceViewPositionUpdateMethod = GetMethodIDOrDie(env, clazz, - "updateWindowPosition_renderWorker", "(JIIII)V"); + "updateSurfacePosition_renderWorker", "(JIIII)V"); gSurfaceViewPositionLostMethod = GetMethodIDOrDie(env, clazz, - "windowPositionLost_uiRtSync", "(J)V"); + "surfacePositionLost_uiRtSync", "(J)V"); return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index a81901df9a1b0..6fbf49bfee8c1 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -693,7 +693,6 @@ static jboolean nativeGetAnimationFrameStats(JNIEnv* env, jclass clazz, jobject return JNI_TRUE; } - static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong nativeObject, jobject handleObject, jlong frameNumber) { auto ctrl = reinterpret_cast(nativeObject); @@ -702,6 +701,27 @@ static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong nativeO ctrl->deferTransactionUntil(handle, frameNumber); } +static void nativeDeferTransactionUntilSurface(JNIEnv* env, jclass clazz, jlong nativeObject, + jlong surfaceObject, jlong frameNumber) { + auto ctrl = reinterpret_cast(nativeObject); + sp barrier = reinterpret_cast(surfaceObject); + + ctrl->deferTransactionUntil(barrier, frameNumber); +} + +static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong nativeObject, + jobject newParentObject) { + auto ctrl = reinterpret_cast(nativeObject); + sp handle = ibinderForJavaObject(env, newParentObject); + + ctrl->reparentChildren(handle); +} + +static void nativeSeverChildren(JNIEnv* env, jclass clazz, jlong nativeObject) { + auto ctrl = reinterpret_cast(nativeObject); + ctrl->detachChildren(); +} + static void nativeSetOverrideScalingMode(JNIEnv* env, jclass clazz, jlong nativeObject, jint scalingMode) { auto ctrl = reinterpret_cast(nativeObject); @@ -824,6 +844,12 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetDisplayPowerMode }, {"nativeDeferTransactionUntil", "(JLandroid/os/IBinder;J)V", (void*)nativeDeferTransactionUntil }, + {"nativeDeferTransactionUntilSurface", "(JJJ)V", + (void*)nativeDeferTransactionUntilSurface }, + {"nativeReparentChildren", "(JLandroid/os/IBinder;)V", + (void*)nativeReparentChildren } , + {"nativeSeverChildren", "(J)V", + (void*)nativeSeverChildren } , {"nativeSetOverrideScalingMode", "(JI)V", (void*)nativeSetOverrideScalingMode }, {"nativeGetHandle", "(J)Landroid/os/IBinder;", diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp index dad6958560c0a..508d897955694 100644 --- a/core/jni/android_view_SurfaceSession.cpp +++ b/core/jni/android_view_SurfaceSession.cpp @@ -24,6 +24,7 @@ #include #include +#include namespace android { @@ -45,6 +46,13 @@ static jlong nativeCreate(JNIEnv* env, jclass clazz) { return reinterpret_cast(client); } +static jlong nativeCreateScoped(JNIEnv* env, jclass clazz, jlong surfaceObject) { + Surface *parent = reinterpret_cast(surfaceObject); + SurfaceComposerClient* client = new SurfaceComposerClient(parent->getIGraphicBufferProducer()); + client->incStrong((void*)nativeCreate); + return reinterpret_cast(client); +} + static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) { SurfaceComposerClient* client = reinterpret_cast(ptr); client->decStrong((void*)nativeCreate); @@ -55,11 +63,12 @@ static void nativeKill(JNIEnv* env, jclass clazz, jlong ptr) { client->dispose(); } - static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ { "nativeCreate", "()J", (void*)nativeCreate }, + { "nativeCreateScoped", "(J)J", + (void*)nativeCreateScoped }, { "nativeDestroy", "(J)V", (void*)nativeDestroy }, { "nativeKill", "(J)V", diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 37eae48a7a119..99edf6ef944ef 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -178,13 +178,9 @@ public: } } // TODO: This is hacky - info.windowInsetLeft = -stagingProperties().getLeft(); - info.windowInsetTop = -stagingProperties().getTop(); info.updateWindowPositions = true; RenderNode::prepareTree(info); info.updateWindowPositions = false; - info.windowInsetLeft = 0; - info.windowInsetTop = 0; info.errorHandler = nullptr; } diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index c6fbe2bd55ded..e39614b6a5ea4 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -91,8 +91,6 @@ public: LayerUpdateQueue* layerUpdateQueue = nullptr; ErrorHandler* errorHandler = nullptr; - int32_t windowInsetLeft = 0; - int32_t windowInsetTop = 0; bool updateWindowPositions = false; struct Out { diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java index aee9d38e0a270..e5af35711311c 100644 --- a/media/java/android/media/tv/TvView.java +++ b/media/java/android/media/tv/TvView.java @@ -776,8 +776,8 @@ public class TvView extends ViewGroup { mSurface = null; mSurfaceView = new SurfaceView(getContext(), mAttrs, mDefStyleAttr) { @Override - protected void updateWindow() { - super.updateWindow(); + protected void updateSurface() { + super.updateSurface(); relayoutSessionOverlayView(); }}; // The surface view's content should be treated as secure all the time. diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 647adbf960a52..4aa013ae90c1e 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -785,6 +785,16 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (canFreezeBounds()) { freezeBounds(); } + + // In the process of tearing down before relaunching, the app will + // try and clean up it's child surfaces. We need to prevent this from + // happening, so we sever the children, transfering their ownership + // from the client it-self to the parent surface (owned by us). + for (int i = mChildren.size() - 1; i >= 0; i--) { + final WindowState w = mChildren.get(i); + w.mWinAnimator.detachChildren(); + } + mPendingRelaunchCount++; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 8b622368e5162..66ec8f0c61769 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2199,6 +2199,15 @@ public class WindowManagerService extends IWindowManager.Stub if (mAccessibilityController != null && win.getDisplayId() == DEFAULT_DISPLAY) { mAccessibilityController.onWindowTransitionLocked(win, transit); } + + // When we start the exit animation we take the Surface from the client + // so it will stop perturbing it. We need to likewise takeaway the SurfaceFlinger + // side child surfaces, so they will remain preserved in their current state + // (rather than be cleaned up immediately by the app code). + SurfaceControl.openTransaction(); + winAnimator.detachChildren(); + SurfaceControl.closeTransaction(); + return focusMayChange; } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 6d572d78b9d71..48060686a1de4 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1532,6 +1532,13 @@ class WindowState extends WindowContainer implements WindowManagerP return changed; } + // Next up we will notify the client that it's visibility has changed. + // We need to prevent it from destroying child surfaces until + // the animation has finished. + if (!visible && isVisibleNow()) { + mWinAnimator.detachChildren(); + } + if (visible != isVisibleNow()) { if (!runningAppAnimation) { final AccessibilityController accessibilityController = diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 98598e1654dc8..4b7133836db85 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -566,6 +566,20 @@ class WindowStateAnimator { if (!mDestroyPreservedSurfaceUponRedraw) { return; } + if (mSurfaceController != null) { + if (mPendingDestroySurface != null) { + // If we are preserving a surface but we aren't relaunching that means + // we are just doing an in-place switch. In that case any SurfaceFlinger side + // child layers need to be reparented to the new surface to make this + // transparent to the app. + if (mWin.mAppToken == null || mWin.mAppToken.isRelaunching() == false) { + SurfaceControl.openTransaction(); + mPendingDestroySurface.reparentChildrenInTransaction(mSurfaceController); + SurfaceControl.closeTransaction(); + } + } + } + destroyDeferredSurfaceLocked(); mDestroyPreservedSurfaceUponRedraw = false; } @@ -1965,4 +1979,10 @@ class WindowStateAnimator { } return mForceScaleUntilResize; } + + void detachChildren() { + if (mSurfaceController != null) { + mSurfaceController.detachChildren(); + } + } } diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index f8e74284fafd1..f7d3343831bf0 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -135,6 +135,20 @@ class WindowSurfaceController { } } + void reparentChildrenInTransaction(WindowSurfaceController other) { + if (SHOW_TRANSACTIONS) Slog.i(TAG, "REPARENT from: " + this + " to: " + other); + if ((mSurfaceControl != null) && (other.mSurfaceControl != null)) { + mSurfaceControl.reparentChildren(other.getHandle()); + } + } + + void detachChildren() { + if (SHOW_TRANSACTIONS) Slog.i(TAG, "SEVER CHILDREN"); + if (mSurfaceControl != null) { + mSurfaceControl.detachChildren(); + } + } + void hideInTransaction(String reason) { if (SHOW_TRANSACTIONS) logSurface("HIDE ( " + reason + " )", null); mHiddenForOtherReasons = true;