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