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