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