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 
SurfaceView(Context context)184     public SurfaceView(Context context) {
185         this(context, null);
186     }
187 
SurfaceView(Context context, AttributeSet attrs)188     public SurfaceView(Context context, AttributeSet attrs) {
189         this(context, attrs, 0);
190     }
191 
SurfaceView(Context context, AttributeSet attrs, int defStyleAttr)192     public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
193         this(context, attrs, defStyleAttr, 0);
194     }
195 
SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)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      */
getHolder()209     public SurfaceHolder getHolder() {
210         return mSurfaceHolder;
211     }
212 
updateRequestedVisibility()213     private void updateRequestedVisibility() {
214         mRequestedVisible = mViewVisibility && mWindowVisibility && !mWindowStopped;
215     }
216 
217     /** @hide */
218     @Override
windowStopped(boolean stopped)219     public void windowStopped(boolean stopped) {
220         mWindowStopped = stopped;
221         updateRequestedVisibility();
222         updateSurface();
223     }
224 
225     @Override
onAttachedToWindow()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
onWindowVisibilityChanged(int visibility)246     protected void onWindowVisibilityChanged(int visibility) {
247         super.onWindowVisibilityChanged(visibility);
248         mWindowVisibility = visibility == VISIBLE;
249         updateRequestedVisibility();
250         updateSurface();
251     }
252 
253     @Override
setVisibility(int visibility)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 
performDrawFinished()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 
notifyDrawFinished()285     void notifyDrawFinished() {
286         ViewRootImpl viewRoot = getViewRootImpl();
287         if (viewRoot != null) {
288             viewRoot.pendingDrawFinished();
289         }
290         mPendingReportDraws--;
291     }
292 
293     @Override
onDetachedFromWindow()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
onMeasure(int widthMeasureSpec, int heightMeasureSpec)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
setFrame(int left, int top, int right, int bottom)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
gatherTransparentRegion(Region region)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
draw(Canvas canvas)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
dispatchDraw(Canvas canvas)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      */
setZOrderMediaOverlay(boolean isMediaOverlay)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      */
setZOrderOnTop(boolean onTop)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      */
setSecure(boolean isSecure)449     public void setSecure(boolean isSecure) {
450         if (isSecure) {
451             mSurfaceFlags |= SurfaceControl.SECURE;
452         } else {
453             mSurfaceFlags &= ~SurfaceControl.SECURE;
454         }
455     }
456 
updateOpaqueFlag()457     private void updateOpaqueFlag() {
458         if (!PixelFormat.formatHasAlpha(mRequestedFormat)) {
459             mSurfaceFlags |= SurfaceControl.OPAQUE;
460         } else {
461             mSurfaceFlags &= ~SurfaceControl.OPAQUE;
462         }
463     }
464 
getParentSurfaceInsets()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 */
updateSurface()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 
onDrawFinished()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      */
applyChildSurfaceTransaction_renderWorker(SurfaceControl.Transaction t, Surface viewRootSurface, long nextViewRootFrameNumber)780     protected void applyChildSurfaceTransaction_renderWorker(SurfaceControl.Transaction t,
781             Surface viewRootSurface, long nextViewRootFrameNumber) {
782     }
783 
applySurfaceTransforms(SurfaceControl surface, Rect position, long frameNumber)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 
setParentSpaceRectangle(Rect position, long frameNumber)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      */
updateSurfacePosition_renderWorker(long frameNumber, int left, int top, int right, int bottom)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      */
surfacePositionLost_uiRtSync(long frameNumber)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 
getSurfaceCallbacks()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 
runOnUiThread(Runnable runnable)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      */
isFixedSize()912     public boolean isFixedSize() {
913         return (mRequestedWidth != -1 || mRequestedHeight != -1);
914     }
915 
isAboveParent()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      */
setResizeBackgroundColor(int bgColor)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 
SurfaceControlWithBackground(String name, boolean opaque, SurfaceControl.Builder b)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
setAlpha(float alpha)1121         public void setAlpha(float alpha) {
1122             super.setAlpha(alpha);
1123             mBackgroundControl.setAlpha(alpha);
1124         }
1125 
1126         @Override
setLayer(int zorder)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
setPosition(float x, float y)1134         public void setPosition(float x, float y) {
1135             super.setPosition(x, y);
1136             mBackgroundControl.setPosition(x, y);
1137         }
1138 
1139         @Override
setSize(int w, int h)1140         public void setSize(int w, int h) {
1141             super.setSize(w, h);
1142             mBackgroundControl.setSize(w, h);
1143         }
1144 
1145         @Override
setWindowCrop(Rect crop)1146         public void setWindowCrop(Rect crop) {
1147             super.setWindowCrop(crop);
1148             mBackgroundControl.setWindowCrop(crop);
1149         }
1150 
1151         @Override
setFinalCrop(Rect crop)1152         public void setFinalCrop(Rect crop) {
1153             super.setFinalCrop(crop);
1154             mBackgroundControl.setFinalCrop(crop);
1155         }
1156 
1157         @Override
setLayerStack(int layerStack)1158         public void setLayerStack(int layerStack) {
1159             super.setLayerStack(layerStack);
1160             mBackgroundControl.setLayerStack(layerStack);
1161         }
1162 
1163         @Override
setOpaque(boolean isOpaque)1164         public void setOpaque(boolean isOpaque) {
1165             super.setOpaque(isOpaque);
1166             mOpaque = isOpaque;
1167             updateBackgroundVisibility();
1168         }
1169 
1170         @Override
setSecure(boolean isSecure)1171         public void setSecure(boolean isSecure) {
1172             super.setSecure(isSecure);
1173         }
1174 
1175         @Override
setMatrix(float dsdx, float dtdx, float dsdy, float dtdy)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
hide()1182         public void hide() {
1183             super.hide();
1184             mVisible = false;
1185             updateBackgroundVisibility();
1186         }
1187 
1188         @Override
show()1189         public void show() {
1190             super.show();
1191             mVisible = true;
1192             updateBackgroundVisibility();
1193         }
1194 
1195         @Override
destroy()1196         public void destroy() {
1197             super.destroy();
1198             mBackgroundControl.destroy();
1199          }
1200 
1201         @Override
release()1202         public void release() {
1203             super.release();
1204             mBackgroundControl.release();
1205         }
1206 
1207         @Override
setTransparentRegionHint(Region region)1208         public void setTransparentRegionHint(Region region) {
1209             super.setTransparentRegionHint(region);
1210             mBackgroundControl.setTransparentRegionHint(region);
1211         }
1212 
1213         @Override
deferTransactionUntil(IBinder handle, long frame)1214         public void deferTransactionUntil(IBinder handle, long frame) {
1215             super.deferTransactionUntil(handle, frame);
1216             mBackgroundControl.deferTransactionUntil(handle, frame);
1217         }
1218 
1219         @Override
deferTransactionUntil(Surface barrier, long frame)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. */
setBackgroundColor(int bgColor)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 
updateBackgroundVisibility()1238         void updateBackgroundVisibility() {
1239             if (mOpaque && mVisible) {
1240                 mBackgroundControl.show();
1241             } else {
1242                 mBackgroundControl.hide();
1243             }
1244         }
1245     }
1246 }
1247