Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.view;
     18 
     19 import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_OVERLAY_SUBLAYER;
     20 import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_SUBLAYER;
     21 import static android.view.WindowManagerPolicyConstants.APPLICATION_PANEL_SUBLAYER;
     22 
     23 import android.content.Context;
     24 import android.content.res.CompatibilityInfo.Translator;
     25 import android.content.res.Configuration;
     26 import android.graphics.Canvas;
     27 import android.graphics.Color;
     28 import android.graphics.PixelFormat;
     29 import android.graphics.PorterDuff;
     30 import android.graphics.Rect;
     31 import android.graphics.Region;
     32 import android.os.Build;
     33 import android.os.Handler;
     34 import android.os.IBinder;
     35 import android.os.Looper;
     36 import android.os.SystemClock;
     37 import android.util.AttributeSet;
     38 import android.util.Log;
     39 
     40 import com.android.internal.view.SurfaceCallbackHelper;
     41 
     42 import java.util.ArrayList;
     43 import java.util.concurrent.locks.ReentrantLock;
     44 
     45 /**
     46  * Provides a dedicated drawing surface embedded inside of a view hierarchy.
     47  * You can control the format of this surface and, if you like, its size; the
     48  * SurfaceView takes care of placing the surface at the correct location on the
     49  * screen
     50  *
     51  * <p>The surface is Z ordered so that it is behind the window holding its
     52  * SurfaceView; the SurfaceView punches a hole in its window to allow its
     53  * surface to be displayed. The view hierarchy will take care of correctly
     54  * compositing with the Surface any siblings of the SurfaceView that would
     55  * normally appear on top of it. This can be used to place overlays such as
     56  * buttons on top of the Surface, though note however that it can have an
     57  * impact on performance since a full alpha-blended composite will be performed
     58  * each time the Surface changes.
     59  *
     60  * <p> The transparent region that makes the surface visible is based on the
     61  * layout positions in the view hierarchy. If the post-layout transform
     62  * properties are used to draw a sibling view on top of the SurfaceView, the
     63  * view may not be properly composited with the surface.
     64  *
     65  * <p>Access to the underlying surface is provided via the SurfaceHolder interface,
     66  * which can be retrieved by calling {@link #getHolder}.
     67  *
     68  * <p>The Surface will be created for you while the SurfaceView's window is
     69  * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated}
     70  * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the
     71  * Surface is created and destroyed as the window is shown and hidden.
     72  *
     73  * <p>One of the purposes of this class is to provide a surface in which a
     74  * secondary thread can render into the screen. If you are going to use it
     75  * this way, you need to be aware of some threading semantics:
     76  *
     77  * <ul>
     78  * <li> All SurfaceView and
     79  * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called
     80  * from the thread running the SurfaceView's window (typically the main thread
     81  * of the application). They thus need to correctly synchronize with any
     82  * state that is also touched by the drawing thread.
     83  * <li> You must ensure that the drawing thread only touches the underlying
     84  * Surface while it is valid -- between
     85  * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()}
     86  * and
     87  * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
     88  * </ul>
     89  *
     90  * <p class="note"><strong>Note:</strong> Starting in platform version
     91  * {@link android.os.Build.VERSION_CODES#N}, SurfaceView's window position is
     92  * updated synchronously with other View rendering. This means that translating
     93  * and scaling a SurfaceView on screen will not cause rendering artifacts. Such
     94  * artifacts may occur on previous versions of the platform when its window is
     95  * positioned asynchronously.</p>
     96  */
     97 public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallback {
     98     private static final String TAG = "SurfaceView";
     99     private static final boolean DEBUG = false;
    100 
    101     final ArrayList<SurfaceHolder.Callback> mCallbacks
    102             = new ArrayList<SurfaceHolder.Callback>();
    103 
    104     final int[] mLocation = new int[2];
    105 
    106     final ReentrantLock mSurfaceLock = new ReentrantLock();
    107     final Surface mSurface = new Surface();       // Current surface in use
    108     boolean mDrawingStopped = true;
    109     // We use this to track if the application has produced a frame
    110     // in to the Surface. Up until that point, we should be careful not to punch
    111     // holes.
    112     boolean mDrawFinished = false;
    113 
    114     final Rect mScreenRect = new Rect();
    115     SurfaceSession mSurfaceSession;
    116 
    117     SurfaceControlWithBackground mSurfaceControl;
    118     // In the case of format changes we switch out the surface in-place
    119     // we need to preserve the old one until the new one has drawn.
    120     SurfaceControl mDeferredDestroySurfaceControl;
    121     final Rect mTmpRect = new Rect();
    122     final Configuration mConfiguration = new Configuration();
    123 
    124     int mSubLayer = APPLICATION_MEDIA_SUBLAYER;
    125 
    126     boolean mIsCreating = false;
    127     private volatile boolean mRtHandlingPositionUpdates = false;
    128 
    129     private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
    130             = new ViewTreeObserver.OnScrollChangedListener() {
    131                     @Override
    132                     public void onScrollChanged() {
    133                         updateSurface();
    134                     }
    135             };
    136 
    137     private final ViewTreeObserver.OnPreDrawListener mDrawListener =
    138             new ViewTreeObserver.OnPreDrawListener() {
    139                 @Override
    140                 public boolean onPreDraw() {
    141                     // reposition ourselves where the surface is
    142                     mHaveFrame = getWidth() > 0 && getHeight() > 0;
    143                     updateSurface();
    144                     return true;
    145                 }
    146             };
    147 
    148     boolean mRequestedVisible = false;
    149     boolean mWindowVisibility = false;
    150     boolean mLastWindowVisibility = false;
    151     boolean mViewVisibility = false;
    152     boolean mWindowStopped = false;
    153 
    154     int mRequestedWidth = -1;
    155     int mRequestedHeight = -1;
    156     /* Set SurfaceView's format to 565 by default to maintain backward
    157      * compatibility with applications assuming this format.
    158      */
    159     int mRequestedFormat = PixelFormat.RGB_565;
    160 
    161     boolean mHaveFrame = false;
    162     boolean mSurfaceCreated = false;
    163     long mLastLockTime = 0;
    164 
    165     boolean mVisible = false;
    166     int mWindowSpaceLeft = -1;
    167     int mWindowSpaceTop = -1;
    168     int mSurfaceWidth = -1;
    169     int mSurfaceHeight = -1;
    170     int mFormat = -1;
    171     final Rect mSurfaceFrame = new Rect();
    172     int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
    173     private Translator mTranslator;
    174 
    175     private boolean mGlobalListenersAdded;
    176     private boolean mAttachedToWindow;
    177 
    178     private int mSurfaceFlags = SurfaceControl.HIDDEN;
    179 
    180     private int mPendingReportDraws;
    181 
    182     private SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
    183 
    184     public SurfaceView(Context context) {
    185         this(context, null);
    186     }
    187 
    188     public SurfaceView(Context context, AttributeSet attrs) {
    189         this(context, attrs, 0);
    190     }
    191 
    192     public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
    193         this(context, attrs, defStyleAttr, 0);
    194     }
    195 
    196     public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    197         super(context, attrs, defStyleAttr, defStyleRes);
    198         mRenderNode.requestPositionUpdates(this);
    199 
    200         setWillNotDraw(true);
    201     }
    202 
    203     /**
    204      * Return the SurfaceHolder providing access and control over this
    205      * SurfaceView's underlying surface.
    206      *
    207      * @return SurfaceHolder The holder of the surface.
    208      */
    209     public SurfaceHolder getHolder() {
    210         return mSurfaceHolder;
    211     }
    212 
    213     private void updateRequestedVisibility() {
    214         mRequestedVisible = mViewVisibility && mWindowVisibility && !mWindowStopped;
    215     }
    216 
    217     /** @hide */
    218     @Override
    219     public void windowStopped(boolean stopped) {
    220         mWindowStopped = stopped;
    221         updateRequestedVisibility();
    222         updateSurface();
    223     }
    224 
    225     @Override
    226     protected void onAttachedToWindow() {
    227         super.onAttachedToWindow();
    228 
    229         getViewRootImpl().addWindowStoppedCallback(this);
    230         mWindowStopped = false;
    231 
    232         mViewVisibility = getVisibility() == VISIBLE;
    233         updateRequestedVisibility();
    234 
    235         mAttachedToWindow = true;
    236         mParent.requestTransparentRegion(SurfaceView.this);
    237         if (!mGlobalListenersAdded) {
    238             ViewTreeObserver observer = getViewTreeObserver();
    239             observer.addOnScrollChangedListener(mScrollChangedListener);
    240             observer.addOnPreDrawListener(mDrawListener);
    241             mGlobalListenersAdded = true;
    242         }
    243     }
    244 
    245     @Override
    246     protected void onWindowVisibilityChanged(int visibility) {
    247         super.onWindowVisibilityChanged(visibility);
    248         mWindowVisibility = visibility == VISIBLE;
    249         updateRequestedVisibility();
    250         updateSurface();
    251     }
    252 
    253     @Override
    254     public void setVisibility(int visibility) {
    255         super.setVisibility(visibility);
    256         mViewVisibility = visibility == VISIBLE;
    257         boolean newRequestedVisible = mWindowVisibility && mViewVisibility && !mWindowStopped;
    258         if (newRequestedVisible != mRequestedVisible) {
    259             // our base class (View) invalidates the layout only when
    260             // we go from/to the GONE state. However, SurfaceView needs
    261             // to request a re-layout when the visibility changes at all.
    262             // This is needed because the transparent region is computed
    263             // as part of the layout phase, and it changes (obviously) when
    264             // the visibility changes.
    265             requestLayout();
    266         }
    267         mRequestedVisible = newRequestedVisible;
    268         updateSurface();
    269     }
    270 
    271     private void performDrawFinished() {
    272         if (mPendingReportDraws > 0) {
    273             mDrawFinished = true;
    274             if (mAttachedToWindow) {
    275                 notifyDrawFinished();
    276                 invalidate();
    277             }
    278         } else {
    279             Log.e(TAG, System.identityHashCode(this) + "finished drawing"
    280                     + " but no pending report draw (extra call"
    281                     + " to draw completion runnable?)");
    282         }
    283     }
    284 
    285     void notifyDrawFinished() {
    286         ViewRootImpl viewRoot = getViewRootImpl();
    287         if (viewRoot != null) {
    288             viewRoot.pendingDrawFinished();
    289         }
    290         mPendingReportDraws--;
    291     }
    292 
    293     @Override
    294     protected void onDetachedFromWindow() {
    295         ViewRootImpl viewRoot = getViewRootImpl();
    296         // It's possible to create a SurfaceView using the default constructor and never
    297         // attach it to a view hierarchy, this is a common use case when dealing with
    298         // OpenGL. A developer will probably create a new GLSurfaceView, and let it manage
    299         // the lifecycle. Instead of attaching it to a view, he/she can just pass
    300         // the SurfaceHolder forward, most live wallpapers do it.
    301         if (viewRoot != null) {
    302             viewRoot.removeWindowStoppedCallback(this);
    303         }
    304 
    305         mAttachedToWindow = false;
    306         if (mGlobalListenersAdded) {
    307             ViewTreeObserver observer = getViewTreeObserver();
    308             observer.removeOnScrollChangedListener(mScrollChangedListener);
    309             observer.removeOnPreDrawListener(mDrawListener);
    310             mGlobalListenersAdded = false;
    311         }
    312 
    313         while (mPendingReportDraws > 0) {
    314             notifyDrawFinished();
    315         }
    316 
    317         mRequestedVisible = false;
    318 
    319         updateSurface();
    320         if (mSurfaceControl != null) {
    321             mSurfaceControl.destroy();
    322         }
    323         mSurfaceControl = null;
    324 
    325         mHaveFrame = false;
    326 
    327         super.onDetachedFromWindow();
    328     }
    329 
    330     @Override
    331     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    332         int width = mRequestedWidth >= 0
    333                 ? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0)
    334                 : getDefaultSize(0, widthMeasureSpec);
    335         int height = mRequestedHeight >= 0
    336                 ? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0)
    337                 : getDefaultSize(0, heightMeasureSpec);
    338         setMeasuredDimension(width, height);
    339     }
    340 
    341     /** @hide */
    342     @Override
    343     protected boolean setFrame(int left, int top, int right, int bottom) {
    344         boolean result = super.setFrame(left, top, right, bottom);
    345         updateSurface();
    346         return result;
    347     }
    348 
    349     @Override
    350     public boolean gatherTransparentRegion(Region region) {
    351         if (isAboveParent() || !mDrawFinished) {
    352             return super.gatherTransparentRegion(region);
    353         }
    354 
    355         boolean opaque = true;
    356         if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
    357             // this view draws, remove it from the transparent region
    358             opaque = super.gatherTransparentRegion(region);
    359         } else if (region != null) {
    360             int w = getWidth();
    361             int h = getHeight();
    362             if (w>0 && h>0) {
    363                 getLocationInWindow(mLocation);
    364                 // otherwise, punch a hole in the whole hierarchy
    365                 int l = mLocation[0];
    366                 int t = mLocation[1];
    367                 region.op(l, t, l+w, t+h, Region.Op.UNION);
    368             }
    369         }
    370         if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
    371             opaque = false;
    372         }
    373         return opaque;
    374     }
    375 
    376     @Override
    377     public void draw(Canvas canvas) {
    378         if (mDrawFinished && !isAboveParent()) {
    379             // draw() is not called when SKIP_DRAW is set
    380             if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
    381                 // punch a whole in the view-hierarchy below us
    382                 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
    383             }
    384         }
    385         super.draw(canvas);
    386     }
    387 
    388     @Override
    389     protected void dispatchDraw(Canvas canvas) {
    390         if (mDrawFinished && !isAboveParent()) {
    391             // draw() is not called when SKIP_DRAW is set
    392             if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
    393                 // punch a whole in the view-hierarchy below us
    394                 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
    395             }
    396         }
    397         super.dispatchDraw(canvas);
    398     }
    399 
    400     /**
    401      * Control whether the surface view's surface is placed on top of another
    402      * regular surface view in the window (but still behind the window itself).
    403      * This is typically used to place overlays on top of an underlying media
    404      * surface view.
    405      *
    406      * <p>Note that this must be set before the surface view's containing
    407      * window is attached to the window manager.
    408      *
    409      * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
    410      */
    411     public void setZOrderMediaOverlay(boolean isMediaOverlay) {
    412         mSubLayer = isMediaOverlay
    413             ? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER;
    414     }
    415 
    416     /**
    417      * Control whether the surface view's surface is placed on top of its
    418      * window.  Normally it is placed behind the window, to allow it to
    419      * (for the most part) appear to composite with the views in the
    420      * hierarchy.  By setting this, you cause it to be placed above the
    421      * window.  This means that none of the contents of the window this
    422      * SurfaceView is in will be visible on top of its surface.
    423      *
    424      * <p>Note that this must be set before the surface view's containing
    425      * window is attached to the window manager.
    426      *
    427      * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
    428      */
    429     public void setZOrderOnTop(boolean onTop) {
    430         if (onTop) {
    431             mSubLayer = APPLICATION_PANEL_SUBLAYER;
    432         } else {
    433             mSubLayer = APPLICATION_MEDIA_SUBLAYER;
    434         }
    435     }
    436 
    437     /**
    438      * Control whether the surface view's content should be treated as secure,
    439      * preventing it from appearing in screenshots or from being viewed on
    440      * non-secure displays.
    441      *
    442      * <p>Note that this must be set before the surface view's containing
    443      * window is attached to the window manager.
    444      *
    445      * <p>See {@link android.view.Display#FLAG_SECURE} for details.
    446      *
    447      * @param isSecure True if the surface view is secure.
    448      */
    449     public void setSecure(boolean isSecure) {
    450         if (isSecure) {
    451             mSurfaceFlags |= SurfaceControl.SECURE;
    452         } else {
    453             mSurfaceFlags &= ~SurfaceControl.SECURE;
    454         }
    455     }
    456 
    457     private void updateOpaqueFlag() {
    458         if (!PixelFormat.formatHasAlpha(mRequestedFormat)) {
    459             mSurfaceFlags |= SurfaceControl.OPAQUE;
    460         } else {
    461             mSurfaceFlags &= ~SurfaceControl.OPAQUE;
    462         }
    463     }
    464 
    465     private Rect getParentSurfaceInsets() {
    466         final ViewRootImpl root = getViewRootImpl();
    467         if (root == null) {
    468             return null;
    469         } else {
    470             return root.mWindowAttributes.surfaceInsets;
    471         }
    472     }
    473 
    474     /** @hide */
    475     protected void updateSurface() {
    476         if (!mHaveFrame) {
    477             return;
    478         }
    479         ViewRootImpl viewRoot = getViewRootImpl();
    480         if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
    481             return;
    482         }
    483 
    484         mTranslator = viewRoot.mTranslator;
    485         if (mTranslator != null) {
    486             mSurface.setCompatibilityTranslator(mTranslator);
    487         }
    488 
    489         int myWidth = mRequestedWidth;
    490         if (myWidth <= 0) myWidth = getWidth();
    491         int myHeight = mRequestedHeight;
    492         if (myHeight <= 0) myHeight = getHeight();
    493 
    494         final boolean formatChanged = mFormat != mRequestedFormat;
    495         final boolean visibleChanged = mVisible != mRequestedVisible;
    496         final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged)
    497                 && mRequestedVisible;
    498         final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
    499         final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
    500         boolean redrawNeeded = false;
    501 
    502         if (creating || formatChanged || sizeChanged || visibleChanged || windowVisibleChanged) {
    503             getLocationInWindow(mLocation);
    504 
    505             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
    506                     + "Changes: creating=" + creating
    507                     + " format=" + formatChanged + " size=" + sizeChanged
    508                     + " visible=" + visibleChanged
    509                     + " left=" + (mWindowSpaceLeft != mLocation[0])
    510                     + " top=" + (mWindowSpaceTop != mLocation[1]));
    511 
    512             try {
    513                 final boolean visible = mVisible = mRequestedVisible;
    514                 mWindowSpaceLeft = mLocation[0];
    515                 mWindowSpaceTop = mLocation[1];
    516                 mSurfaceWidth = myWidth;
    517                 mSurfaceHeight = myHeight;
    518                 mFormat = mRequestedFormat;
    519                 mLastWindowVisibility = mWindowVisibility;
    520 
    521                 mScreenRect.left = mWindowSpaceLeft;
    522                 mScreenRect.top = mWindowSpaceTop;
    523                 mScreenRect.right = mWindowSpaceLeft + getWidth();
    524                 mScreenRect.bottom = mWindowSpaceTop + getHeight();
    525                 if (mTranslator != null) {
    526                     mTranslator.translateRectInAppWindowToScreen(mScreenRect);
    527                 }
    528 
    529                 final Rect surfaceInsets = getParentSurfaceInsets();
    530                 mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
    531 
    532                 if (creating) {
    533                     mSurfaceSession = new SurfaceSession(viewRoot.mSurface);
    534                     mDeferredDestroySurfaceControl = mSurfaceControl;
    535 
    536                     updateOpaqueFlag();
    537                     final String name = "SurfaceView - " + viewRoot.getTitle().toString();
    538 
    539                     mSurfaceControl = new SurfaceControlWithBackground(
    540                             name,
    541                             (mSurfaceFlags & SurfaceControl.OPAQUE) != 0,
    542                             new SurfaceControl.Builder(mSurfaceSession)
    543                                     .setSize(mSurfaceWidth, mSurfaceHeight)
    544                                     .setFormat(mFormat)
    545                                     .setFlags(mSurfaceFlags));
    546                 } else if (mSurfaceControl == null) {
    547                     return;
    548                 }
    549 
    550                 boolean realSizeChanged = false;
    551 
    552                 mSurfaceLock.lock();
    553                 try {
    554                     mDrawingStopped = !visible;
    555 
    556                     if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
    557                             + "Cur surface: " + mSurface);
    558 
    559                     SurfaceControl.openTransaction();
    560                     try {
    561                         mSurfaceControl.setLayer(mSubLayer);
    562                         if (mViewVisibility) {
    563                             mSurfaceControl.show();
    564                         } else {
    565                             mSurfaceControl.hide();
    566                         }
    567 
    568                         // While creating the surface, we will set it's initial
    569                         // geometry. Outside of that though, we should generally
    570                         // leave it to the RenderThread.
    571                         //
    572                         // There is one more case when the buffer size changes we aren't yet
    573                         // prepared to sync (as even following the transaction applying
    574                         // we still need to latch a buffer).
    575                         // b/28866173
    576                         if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
    577                             mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top);
    578                             mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
    579                                     0.0f, 0.0f,
    580                                     mScreenRect.height() / (float) mSurfaceHeight);
    581                         }
    582                         if (sizeChanged) {
    583                             mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight);
    584                         }
    585                     } finally {
    586                         SurfaceControl.closeTransaction();
    587                     }
    588 
    589                     if (sizeChanged || creating) {
    590                         redrawNeeded = true;
    591                     }
    592 
    593                     mSurfaceFrame.left = 0;
    594                     mSurfaceFrame.top = 0;
    595                     if (mTranslator == null) {
    596                         mSurfaceFrame.right = mSurfaceWidth;
    597                         mSurfaceFrame.bottom = mSurfaceHeight;
    598                     } else {
    599                         float appInvertedScale = mTranslator.applicationInvertedScale;
    600                         mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
    601                         mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
    602                     }
    603 
    604                     final int surfaceWidth = mSurfaceFrame.right;
    605                     final int surfaceHeight = mSurfaceFrame.bottom;
    606                     realSizeChanged = mLastSurfaceWidth != surfaceWidth
    607                             || mLastSurfaceHeight != surfaceHeight;
    608                     mLastSurfaceWidth = surfaceWidth;
    609                     mLastSurfaceHeight = surfaceHeight;
    610                 } finally {
    611                     mSurfaceLock.unlock();
    612                 }
    613 
    614                 try {
    615                     redrawNeeded |= visible && !mDrawFinished;
    616 
    617                     SurfaceHolder.Callback callbacks[] = null;
    618 
    619                     final boolean surfaceChanged = creating;
    620                     if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
    621                         mSurfaceCreated = false;
    622                         if (mSurface.isValid()) {
    623                             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
    624                                     + "visibleChanged -- surfaceDestroyed");
    625                             callbacks = getSurfaceCallbacks();
    626                             for (SurfaceHolder.Callback c : callbacks) {
    627                                 c.surfaceDestroyed(mSurfaceHolder);
    628                             }
    629                             // Since Android N the same surface may be reused and given to us
    630                             // again by the system server at a later point. However
    631                             // as we didn't do this in previous releases, clients weren't
    632                             // necessarily required to clean up properly in
    633                             // surfaceDestroyed. This leads to problems for example when
    634                             // clients don't destroy their EGL context, and try
    635                             // and create a new one on the same surface following reuse.
    636                             // Since there is no valid use of the surface in-between
    637                             // surfaceDestroyed and surfaceCreated, we force a disconnect,
    638                             // so the next connect will always work if we end up reusing
    639                             // the surface.
    640                             if (mSurface.isValid()) {
    641                                 mSurface.forceScopedDisconnect();
    642                             }
    643                         }
    644                     }
    645 
    646                     if (creating) {
    647                         mSurface.copyFrom(mSurfaceControl);
    648                     }
    649 
    650                     if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion
    651                             < Build.VERSION_CODES.O) {
    652                         // Some legacy applications use the underlying native {@link Surface} object
    653                         // as a key to whether anything has changed. In these cases, updates to the
    654                         // existing {@link Surface} will be ignored when the size changes.
    655                         // Therefore, we must explicitly recreate the {@link Surface} in these
    656                         // cases.
    657                         mSurface.createFrom(mSurfaceControl);
    658                     }
    659 
    660                     if (visible && mSurface.isValid()) {
    661                         if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
    662                             mSurfaceCreated = true;
    663                             mIsCreating = true;
    664                             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
    665                                     + "visibleChanged -- surfaceCreated");
    666                             if (callbacks == null) {
    667                                 callbacks = getSurfaceCallbacks();
    668                             }
    669                             for (SurfaceHolder.Callback c : callbacks) {
    670                                 c.surfaceCreated(mSurfaceHolder);
    671                             }
    672                         }
    673                         if (creating || formatChanged || sizeChanged
    674                                 || visibleChanged || realSizeChanged) {
    675                             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
    676                                     + "surfaceChanged -- format=" + mFormat
    677                                     + " w=" + myWidth + " h=" + myHeight);
    678                             if (callbacks == null) {
    679                                 callbacks = getSurfaceCallbacks();
    680                             }
    681                             for (SurfaceHolder.Callback c : callbacks) {
    682                                 c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
    683                             }
    684                         }
    685                         if (redrawNeeded) {
    686                             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
    687                                     + "surfaceRedrawNeeded");
    688                             if (callbacks == null) {
    689                                 callbacks = getSurfaceCallbacks();
    690                             }
    691 
    692                             mPendingReportDraws++;
    693                             viewRoot.drawPending();
    694                             SurfaceCallbackHelper sch =
    695                                     new SurfaceCallbackHelper(this::onDrawFinished);
    696                             sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
    697                         }
    698                     }
    699                 } finally {
    700                     mIsCreating = false;
    701                     if (mSurfaceControl != null && !mSurfaceCreated) {
    702                         mSurface.release();
    703 
    704                         mSurfaceControl.destroy();
    705                         mSurfaceControl = null;
    706                     }
    707                 }
    708             } catch (Exception ex) {
    709                 Log.e(TAG, "Exception configuring surface", ex);
    710             }
    711             if (DEBUG) Log.v(
    712                 TAG, "Layout: x=" + mScreenRect.left + " y=" + mScreenRect.top
    713                 + " w=" + mScreenRect.width() + " h=" + mScreenRect.height()
    714                 + ", frame=" + mSurfaceFrame);
    715         } else {
    716             // Calculate the window position in case RT loses the window
    717             // and we need to fallback to a UI-thread driven position update
    718             getLocationInSurface(mLocation);
    719             final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
    720                     || mWindowSpaceTop != mLocation[1];
    721             final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
    722                     || getHeight() != mScreenRect.height();
    723             if (positionChanged || layoutSizeChanged) { // Only the position has changed
    724                 mWindowSpaceLeft = mLocation[0];
    725                 mWindowSpaceTop = mLocation[1];
    726                 // For our size changed check, we keep mScreenRect.width() and mScreenRect.height()
    727                 // in view local space.
    728                 mLocation[0] = getWidth();
    729                 mLocation[1] = getHeight();
    730 
    731                 mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop,
    732                         mWindowSpaceLeft + mLocation[0], mWindowSpaceTop + mLocation[1]);
    733 
    734                 if (mTranslator != null) {
    735                     mTranslator.translateRectInAppWindowToScreen(mScreenRect);
    736                 }
    737 
    738                 if (mSurfaceControl == null) {
    739                     return;
    740                 }
    741 
    742                 if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
    743                     try {
    744                         if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition UI, " +
    745                                 "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
    746                                 mScreenRect.left, mScreenRect.top,
    747                                 mScreenRect.right, mScreenRect.bottom));
    748                         setParentSpaceRectangle(mScreenRect, -1);
    749                     } catch (Exception ex) {
    750                         Log.e(TAG, "Exception configuring surface", ex);
    751                     }
    752                 }
    753             }
    754         }
    755     }
    756 
    757     private void onDrawFinished() {
    758         if (DEBUG) {
    759             Log.i(TAG, System.identityHashCode(this) + " "
    760                     + "finishedDrawing");
    761         }
    762 
    763         if (mDeferredDestroySurfaceControl != null) {
    764             mDeferredDestroySurfaceControl.destroy();
    765             mDeferredDestroySurfaceControl = null;
    766         }
    767 
    768         runOnUiThread(() -> {
    769             performDrawFinished();
    770         });
    771     }
    772 
    773     /**
    774      * A place to over-ride for applying child-surface transactions.
    775      * These can be synchronized with the viewroot surface using deferTransaction.
    776      *
    777      * Called from RenderWorker while UI thread is paused.
    778      * @hide
    779      */
    780     protected void applyChildSurfaceTransaction_renderWorker(SurfaceControl.Transaction t,
    781             Surface viewRootSurface, long nextViewRootFrameNumber) {
    782     }
    783 
    784     private void applySurfaceTransforms(SurfaceControl surface, Rect position, long frameNumber) {
    785         if (frameNumber > 0) {
    786             final ViewRootImpl viewRoot = getViewRootImpl();
    787 
    788             mRtTransaction.deferTransactionUntilSurface(surface, viewRoot.mSurface,
    789                     frameNumber);
    790         }
    791 
    792         mRtTransaction.setPosition(surface, position.left, position.top);
    793         mRtTransaction.setMatrix(surface,
    794                 position.width() / (float) mSurfaceWidth,
    795                 0.0f, 0.0f,
    796                 position.height() / (float) mSurfaceHeight);
    797     }
    798 
    799     private void setParentSpaceRectangle(Rect position, long frameNumber) {
    800         final ViewRootImpl viewRoot = getViewRootImpl();
    801 
    802         applySurfaceTransforms(mSurfaceControl, position, frameNumber);
    803         applySurfaceTransforms(mSurfaceControl.mBackgroundControl, position, frameNumber);
    804 
    805         applyChildSurfaceTransaction_renderWorker(mRtTransaction, viewRoot.mSurface,
    806                 frameNumber);
    807 
    808         mRtTransaction.apply();
    809     }
    810 
    811     private Rect mRTLastReportedPosition = new Rect();
    812 
    813     /**
    814      * Called by native by a Rendering Worker thread to update the window position
    815      * @hide
    816      */
    817     public final void updateSurfacePosition_renderWorker(long frameNumber,
    818             int left, int top, int right, int bottom) {
    819         if (mSurfaceControl == null) {
    820             return;
    821         }
    822 
    823         // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
    824         // its 2nd frame if RenderThread is running slowly could potentially see
    825         // this as false, enter the branch, get pre-empted, then this comes along
    826         // and reports a new position, then the UI thread resumes and reports
    827         // its position. This could therefore be de-sync'd in that interval, but
    828         // the synchronization would violate the rule that RT must never block
    829         // on the UI thread which would open up potential deadlocks. The risk of
    830         // a single-frame desync is therefore preferable for now.
    831         mRtHandlingPositionUpdates = true;
    832         if (mRTLastReportedPosition.left == left
    833                 && mRTLastReportedPosition.top == top
    834                 && mRTLastReportedPosition.right == right
    835                 && mRTLastReportedPosition.bottom == bottom) {
    836             return;
    837         }
    838         try {
    839             if (DEBUG) {
    840                 Log.d(TAG, String.format("%d updateSurfacePosition RenderWorker, frameNr = %d, " +
    841                         "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
    842                         frameNumber, left, top, right, bottom));
    843             }
    844             mRTLastReportedPosition.set(left, top, right, bottom);
    845             setParentSpaceRectangle(mRTLastReportedPosition, frameNumber);
    846             // Now overwrite mRTLastReportedPosition with our values
    847         } catch (Exception ex) {
    848             Log.e(TAG, "Exception from repositionChild", ex);
    849         }
    850     }
    851 
    852     /**
    853      * Called by native on RenderThread to notify that the view is no longer in the
    854      * draw tree. UI thread is blocked at this point.
    855      * @hide
    856      */
    857     public final void surfacePositionLost_uiRtSync(long frameNumber) {
    858         if (DEBUG) {
    859             Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
    860                     System.identityHashCode(this), frameNumber));
    861         }
    862         mRTLastReportedPosition.setEmpty();
    863 
    864         if (mSurfaceControl == null) {
    865             return;
    866         }
    867         if (mRtHandlingPositionUpdates) {
    868             mRtHandlingPositionUpdates = false;
    869             // This callback will happen while the UI thread is blocked, so we can
    870             // safely access other member variables at this time.
    871             // So do what the UI thread would have done if RT wasn't handling position
    872             // updates.
    873             if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) {
    874                 try {
    875                     if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition, " +
    876                             "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
    877                             mScreenRect.left, mScreenRect.top,
    878                             mScreenRect.right, mScreenRect.bottom));
    879                     setParentSpaceRectangle(mScreenRect, frameNumber);
    880                 } catch (Exception ex) {
    881                     Log.e(TAG, "Exception configuring surface", ex);
    882                 }
    883             }
    884         }
    885     }
    886 
    887     private SurfaceHolder.Callback[] getSurfaceCallbacks() {
    888         SurfaceHolder.Callback callbacks[];
    889         synchronized (mCallbacks) {
    890             callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
    891             mCallbacks.toArray(callbacks);
    892         }
    893         return callbacks;
    894     }
    895 
    896     private void runOnUiThread(Runnable runnable) {
    897         Handler handler = getHandler();
    898         if (handler != null && handler.getLooper() != Looper.myLooper()) {
    899             handler.post(runnable);
    900         } else {
    901             runnable.run();
    902         }
    903     }
    904 
    905     /**
    906      * Check to see if the surface has fixed size dimensions or if the surface's
    907      * dimensions are dimensions are dependent on its current layout.
    908      *
    909      * @return true if the surface has dimensions that are fixed in size
    910      * @hide
    911      */
    912     public boolean isFixedSize() {
    913         return (mRequestedWidth != -1 || mRequestedHeight != -1);
    914     }
    915 
    916     private boolean isAboveParent() {
    917         return mSubLayer >= 0;
    918     }
    919 
    920     /**
    921      * Set an opaque background color to use with this {@link SurfaceView} when it's being resized
    922      * and size of the content hasn't updated yet. This color will fill the expanded area when the
    923      * view becomes larger.
    924      * @param bgColor An opaque color to fill the background. Alpha component will be ignored.
    925      * @hide
    926      */
    927     public void setResizeBackgroundColor(int bgColor) {
    928         mSurfaceControl.setBackgroundColor(bgColor);
    929     }
    930 
    931     private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
    932         private static final String LOG_TAG = "SurfaceHolder";
    933 
    934         @Override
    935         public boolean isCreating() {
    936             return mIsCreating;
    937         }
    938 
    939         @Override
    940         public void addCallback(Callback callback) {
    941             synchronized (mCallbacks) {
    942                 // This is a linear search, but in practice we'll
    943                 // have only a couple callbacks, so it doesn't matter.
    944                 if (mCallbacks.contains(callback) == false) {
    945                     mCallbacks.add(callback);
    946                 }
    947             }
    948         }
    949 
    950         @Override
    951         public void removeCallback(Callback callback) {
    952             synchronized (mCallbacks) {
    953                 mCallbacks.remove(callback);
    954             }
    955         }
    956 
    957         @Override
    958         public void setFixedSize(int width, int height) {
    959             if (mRequestedWidth != width || mRequestedHeight != height) {
    960                 mRequestedWidth = width;
    961                 mRequestedHeight = height;
    962                 requestLayout();
    963             }
    964         }
    965 
    966         @Override
    967         public void setSizeFromLayout() {
    968             if (mRequestedWidth != -1 || mRequestedHeight != -1) {
    969                 mRequestedWidth = mRequestedHeight = -1;
    970                 requestLayout();
    971             }
    972         }
    973 
    974         @Override
    975         public void setFormat(int format) {
    976             // for backward compatibility reason, OPAQUE always
    977             // means 565 for SurfaceView
    978             if (format == PixelFormat.OPAQUE)
    979                 format = PixelFormat.RGB_565;
    980 
    981             mRequestedFormat = format;
    982             if (mSurfaceControl != null) {
    983                 updateSurface();
    984             }
    985         }
    986 
    987         /**
    988          * @deprecated setType is now ignored.
    989          */
    990         @Override
    991         @Deprecated
    992         public void setType(int type) { }
    993 
    994         @Override
    995         public void setKeepScreenOn(boolean screenOn) {
    996             runOnUiThread(() -> SurfaceView.this.setKeepScreenOn(screenOn));
    997         }
    998 
    999         /**
   1000          * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
   1001          *
   1002          * After drawing into the provided {@link Canvas}, the caller must
   1003          * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
   1004          *
   1005          * The caller must redraw the entire surface.
   1006          * @return A canvas for drawing into the surface.
   1007          */
   1008         @Override
   1009         public Canvas lockCanvas() {
   1010             return internalLockCanvas(null, false);
   1011         }
   1012 
   1013         /**
   1014          * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
   1015          *
   1016          * After drawing into the provided {@link Canvas}, the caller must
   1017          * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
   1018          *
   1019          * @param inOutDirty A rectangle that represents the dirty region that the caller wants
   1020          * to redraw.  This function may choose to expand the dirty rectangle if for example
   1021          * the surface has been resized or if the previous contents of the surface were
   1022          * not available.  The caller must redraw the entire dirty region as represented
   1023          * by the contents of the inOutDirty rectangle upon return from this function.
   1024          * The caller may also pass <code>null</code> instead, in the case where the
   1025          * entire surface should be redrawn.
   1026          * @return A canvas for drawing into the surface.
   1027          */
   1028         @Override
   1029         public Canvas lockCanvas(Rect inOutDirty) {
   1030             return internalLockCanvas(inOutDirty, false);
   1031         }
   1032 
   1033         @Override
   1034         public Canvas lockHardwareCanvas() {
   1035             return internalLockCanvas(null, true);
   1036         }
   1037 
   1038         private Canvas internalLockCanvas(Rect dirty, boolean hardware) {
   1039             mSurfaceLock.lock();
   1040 
   1041             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped="
   1042                     + mDrawingStopped + ", surfaceControl=" + mSurfaceControl);
   1043 
   1044             Canvas c = null;
   1045             if (!mDrawingStopped && mSurfaceControl != null) {
   1046                 try {
   1047                     if (hardware) {
   1048                         c = mSurface.lockHardwareCanvas();
   1049                     } else {
   1050                         c = mSurface.lockCanvas(dirty);
   1051                     }
   1052                 } catch (Exception e) {
   1053                     Log.e(LOG_TAG, "Exception locking surface", e);
   1054                 }
   1055             }
   1056 
   1057             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Returned canvas: " + c);
   1058             if (c != null) {
   1059                 mLastLockTime = SystemClock.uptimeMillis();
   1060                 return c;
   1061             }
   1062 
   1063             // If the Surface is not ready to be drawn, then return null,
   1064             // but throttle calls to this function so it isn't called more
   1065             // than every 100ms.
   1066             long now = SystemClock.uptimeMillis();
   1067             long nextTime = mLastLockTime + 100;
   1068             if (nextTime > now) {
   1069                 try {
   1070                     Thread.sleep(nextTime-now);
   1071                 } catch (InterruptedException e) {
   1072                 }
   1073                 now = SystemClock.uptimeMillis();
   1074             }
   1075             mLastLockTime = now;
   1076             mSurfaceLock.unlock();
   1077 
   1078             return null;
   1079         }
   1080 
   1081         /**
   1082          * Posts the new contents of the {@link Canvas} to the surface and
   1083          * releases the {@link Canvas}.
   1084          *
   1085          * @param canvas The canvas previously obtained from {@link #lockCanvas}.
   1086          */
   1087         @Override
   1088         public void unlockCanvasAndPost(Canvas canvas) {
   1089             mSurface.unlockCanvasAndPost(canvas);
   1090             mSurfaceLock.unlock();
   1091         }
   1092 
   1093         @Override
   1094         public Surface getSurface() {
   1095             return mSurface;
   1096         }
   1097 
   1098         @Override
   1099         public Rect getSurfaceFrame() {
   1100             return mSurfaceFrame;
   1101         }
   1102     };
   1103 
   1104     class SurfaceControlWithBackground extends SurfaceControl {
   1105         SurfaceControl mBackgroundControl;
   1106         private boolean mOpaque = true;
   1107         public boolean mVisible = false;
   1108 
   1109         public SurfaceControlWithBackground(String name, boolean opaque, SurfaceControl.Builder b)
   1110                        throws Exception {
   1111             super(b.setName(name).build());
   1112 
   1113             mBackgroundControl = b.setName("Background for -" + name)
   1114                     .setFormat(OPAQUE)
   1115                     .setColorLayer(true)
   1116                     .build();
   1117             mOpaque = opaque;
   1118         }
   1119 
   1120         @Override
   1121         public void setAlpha(float alpha) {
   1122             super.setAlpha(alpha);
   1123             mBackgroundControl.setAlpha(alpha);
   1124         }
   1125 
   1126         @Override
   1127         public void setLayer(int zorder) {
   1128             super.setLayer(zorder);
   1129             // -3 is below all other child layers as SurfaceView never goes below -2
   1130             mBackgroundControl.setLayer(-3);
   1131         }
   1132 
   1133         @Override
   1134         public void setPosition(float x, float y) {
   1135             super.setPosition(x, y);
   1136             mBackgroundControl.setPosition(x, y);
   1137         }
   1138 
   1139         @Override
   1140         public void setSize(int w, int h) {
   1141             super.setSize(w, h);
   1142             mBackgroundControl.setSize(w, h);
   1143         }
   1144 
   1145         @Override
   1146         public void setWindowCrop(Rect crop) {
   1147             super.setWindowCrop(crop);
   1148             mBackgroundControl.setWindowCrop(crop);
   1149         }
   1150 
   1151         @Override
   1152         public void setFinalCrop(Rect crop) {
   1153             super.setFinalCrop(crop);
   1154             mBackgroundControl.setFinalCrop(crop);
   1155         }
   1156 
   1157         @Override
   1158         public void setLayerStack(int layerStack) {
   1159             super.setLayerStack(layerStack);
   1160             mBackgroundControl.setLayerStack(layerStack);
   1161         }
   1162 
   1163         @Override
   1164         public void setOpaque(boolean isOpaque) {
   1165             super.setOpaque(isOpaque);
   1166             mOpaque = isOpaque;
   1167             updateBackgroundVisibility();
   1168         }
   1169 
   1170         @Override
   1171         public void setSecure(boolean isSecure) {
   1172             super.setSecure(isSecure);
   1173         }
   1174 
   1175         @Override
   1176         public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
   1177             super.setMatrix(dsdx, dtdx, dsdy, dtdy);
   1178             mBackgroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy);
   1179         }
   1180 
   1181         @Override
   1182         public void hide() {
   1183             super.hide();
   1184             mVisible = false;
   1185             updateBackgroundVisibility();
   1186         }
   1187 
   1188         @Override
   1189         public void show() {
   1190             super.show();
   1191             mVisible = true;
   1192             updateBackgroundVisibility();
   1193         }
   1194 
   1195         @Override
   1196         public void destroy() {
   1197             super.destroy();
   1198             mBackgroundControl.destroy();
   1199          }
   1200 
   1201         @Override
   1202         public void release() {
   1203             super.release();
   1204             mBackgroundControl.release();
   1205         }
   1206 
   1207         @Override
   1208         public void setTransparentRegionHint(Region region) {
   1209             super.setTransparentRegionHint(region);
   1210             mBackgroundControl.setTransparentRegionHint(region);
   1211         }
   1212 
   1213         @Override
   1214         public void deferTransactionUntil(IBinder handle, long frame) {
   1215             super.deferTransactionUntil(handle, frame);
   1216             mBackgroundControl.deferTransactionUntil(handle, frame);
   1217         }
   1218 
   1219         @Override
   1220         public void deferTransactionUntil(Surface barrier, long frame) {
   1221             super.deferTransactionUntil(barrier, frame);
   1222             mBackgroundControl.deferTransactionUntil(barrier, frame);
   1223         }
   1224 
   1225         /** Set the color to fill the background with. */
   1226         private void setBackgroundColor(int bgColor) {
   1227             final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
   1228                     Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
   1229 
   1230             SurfaceControl.openTransaction();
   1231             try {
   1232                 mBackgroundControl.setColor(colorComponents);
   1233             } finally {
   1234                 SurfaceControl.closeTransaction();
   1235             }
   1236         }
   1237 
   1238         void updateBackgroundVisibility() {
   1239             if (mOpaque && mVisible) {
   1240                 mBackgroundControl.show();
   1241             } else {
   1242                 mBackgroundControl.hide();
   1243             }
   1244         }
   1245     }
   1246 }
   1247