• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.internal.view.BaseIWindow;
20 
21 import android.content.Context;
22 import android.content.res.Configuration;
23 import android.content.res.CompatibilityInfo.Translator;
24 import android.graphics.Canvas;
25 import android.graphics.PixelFormat;
26 import android.graphics.PorterDuff;
27 import android.graphics.Rect;
28 import android.graphics.Region;
29 import android.os.Handler;
30 import android.os.Message;
31 import android.os.RemoteException;
32 import android.os.SystemClock;
33 import android.os.ParcelFileDescriptor;
34 import android.util.AttributeSet;
35 import android.util.Log;
36 
37 import java.lang.ref.WeakReference;
38 import java.util.ArrayList;
39 import java.util.concurrent.locks.ReentrantLock;
40 
41 /**
42  * Provides a dedicated drawing surface embedded inside of a view hierarchy.
43  * You can control the format of this surface and, if you like, its size; the
44  * SurfaceView takes care of placing the surface at the correct location on the
45  * screen
46  *
47  * <p>The surface is Z ordered so that it is behind the window holding its
48  * SurfaceView; the SurfaceView punches a hole in its window to allow its
49  * surface to be displayed. The view hierarchy will take care of correctly
50  * compositing with the Surface any siblings of the SurfaceView that would
51  * normally appear on top of it. This can be used to place overlays such as
52  * buttons on top of the Surface, though note however that it can have an
53  * impact on performance since a full alpha-blended composite will be performed
54  * each time the Surface changes.
55  *
56  * <p> The transparent region that makes the surface visible is based on the
57  * layout positions in the view hierarchy. If the post-layout transform
58  * properties are used to draw a sibling view on top of the SurfaceView, the
59  * view may not be properly composited with the surface.
60  *
61  * <p>Access to the underlying surface is provided via the SurfaceHolder interface,
62  * which can be retrieved by calling {@link #getHolder}.
63  *
64  * <p>The Surface will be created for you while the SurfaceView's window is
65  * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated}
66  * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the
67  * Surface is created and destroyed as the window is shown and hidden.
68  *
69  * <p>One of the purposes of this class is to provide a surface in which a
70  * secondary thread can render into the screen. If you are going to use it
71  * this way, you need to be aware of some threading semantics:
72  *
73  * <ul>
74  * <li> All SurfaceView and
75  * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called
76  * from the thread running the SurfaceView's window (typically the main thread
77  * of the application). They thus need to correctly synchronize with any
78  * state that is also touched by the drawing thread.
79  * <li> You must ensure that the drawing thread only touches the underlying
80  * Surface while it is valid -- between
81  * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()}
82  * and
83  * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
84  * </ul>
85  *
86  * <p class="note"><strong>Note:</strong> Starting in platform version
87  * {@link android.os.Build.VERSION_CODES#N}, SurfaceView's window position is
88  * updated synchronously with other View rendering. This means that translating
89  * and scaling a SurfaceView on screen will not cause rendering artifacts. Such
90  * artifacts may occur on previous versions of the platform when its window is
91  * positioned asynchronously.</p>
92  */
93 public class SurfaceView extends View {
94     static private final String TAG = "SurfaceView";
95     static private final boolean DEBUG = false;
96 
97     final ArrayList<SurfaceHolder.Callback> mCallbacks
98             = new ArrayList<SurfaceHolder.Callback>();
99 
100     final int[] mLocation = new int[2];
101 
102     final ReentrantLock mSurfaceLock = new ReentrantLock();
103     final Surface mSurface = new Surface();       // Current surface in use
104     final Surface mNewSurface = new Surface();    // New surface we are switching to
105     boolean mDrawingStopped = true;
106 
107     final WindowManager.LayoutParams mLayout
108             = new WindowManager.LayoutParams();
109     IWindowSession mSession;
110     MyWindow mWindow;
111     final Rect mVisibleInsets = new Rect();
112     final Rect mWinFrame = new Rect();
113     final Rect mOverscanInsets = new Rect();
114     final Rect mContentInsets = new Rect();
115     final Rect mStableInsets = new Rect();
116     final Rect mOutsets = new Rect();
117     final Rect mBackdropFrame = new Rect();
118     final Rect mTmpRect = new Rect();
119     final Configuration mConfiguration = new Configuration();
120 
121     static final int KEEP_SCREEN_ON_MSG = 1;
122     static final int GET_NEW_SURFACE_MSG = 2;
123     static final int UPDATE_WINDOW_MSG = 3;
124 
125     int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
126 
127     boolean mIsCreating = false;
128     private volatile boolean mRtHandlingPositionUpdates = false;
129 
130     final Handler mHandler = new Handler() {
131         @Override
132         public void handleMessage(Message msg) {
133             switch (msg.what) {
134                 case KEEP_SCREEN_ON_MSG: {
135                     setKeepScreenOn(msg.arg1 != 0);
136                 } break;
137                 case GET_NEW_SURFACE_MSG: {
138                     handleGetNewSurface();
139                 } break;
140                 case UPDATE_WINDOW_MSG: {
141                     updateWindow(false, false);
142                 } break;
143             }
144         }
145     };
146 
147     private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
148             = new ViewTreeObserver.OnScrollChangedListener() {
149                     @Override
150                     public void onScrollChanged() {
151                         updateWindow(false, false);
152                     }
153             };
154 
155     private final ViewTreeObserver.OnPreDrawListener mDrawListener =
156             new ViewTreeObserver.OnPreDrawListener() {
157                 @Override
158                 public boolean onPreDraw() {
159                     // reposition ourselves where the surface is
160                     mHaveFrame = getWidth() > 0 && getHeight() > 0;
161                     updateWindow(false, false);
162                     return true;
163                 }
164             };
165 
166     boolean mRequestedVisible = false;
167     boolean mWindowVisibility = false;
168     boolean mViewVisibility = false;
169     int mRequestedWidth = -1;
170     int mRequestedHeight = -1;
171     /* Set SurfaceView's format to 565 by default to maintain backward
172      * compatibility with applications assuming this format.
173      */
174     int mRequestedFormat = PixelFormat.RGB_565;
175 
176     boolean mHaveFrame = false;
177     boolean mSurfaceCreated = false;
178     long mLastLockTime = 0;
179 
180     boolean mVisible = false;
181     int mWindowSpaceLeft = -1;
182     int mWindowSpaceTop = -1;
183     int mWindowSpaceWidth = -1;
184     int mWindowSpaceHeight = -1;
185     int mFormat = -1;
186     final Rect mSurfaceFrame = new Rect();
187     int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
188     boolean mUpdateWindowNeeded;
189     boolean mReportDrawNeeded;
190     private Translator mTranslator;
191     private int mWindowInsetLeft;
192     private int mWindowInsetTop;
193 
194     private boolean mGlobalListenersAdded;
195 
SurfaceView(Context context)196     public SurfaceView(Context context) {
197         this(context, null);
198     }
199 
SurfaceView(Context context, AttributeSet attrs)200     public SurfaceView(Context context, AttributeSet attrs) {
201         this(context, attrs, 0);
202     }
203 
SurfaceView(Context context, AttributeSet attrs, int defStyleAttr)204     public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
205         this(context, attrs, defStyleAttr, 0);
206     }
207 
SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)208     public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
209         super(context, attrs, defStyleAttr, defStyleRes);
210 
211         setWillNotDraw(true);
212     }
213 
214     /**
215      * Return the SurfaceHolder providing access and control over this
216      * SurfaceView's underlying surface.
217      *
218      * @return SurfaceHolder The holder of the surface.
219      */
getHolder()220     public SurfaceHolder getHolder() {
221         return mSurfaceHolder;
222     }
223 
224     @Override
onAttachedToWindow()225     protected void onAttachedToWindow() {
226         super.onAttachedToWindow();
227         mParent.requestTransparentRegion(this);
228         mSession = getWindowSession();
229         mLayout.token = getWindowToken();
230         mLayout.setTitle("SurfaceView - " + getViewRootImpl().getTitle());
231         mLayout.packageName = mContext.getOpPackageName();
232         mViewVisibility = getVisibility() == VISIBLE;
233 
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         mRequestedVisible = mWindowVisibility && mViewVisibility;
247         updateWindow(false, false);
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;
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         updateWindow(false, false);
266     }
267 
268     @Override
onDetachedFromWindow()269     protected void onDetachedFromWindow() {
270         if (mGlobalListenersAdded) {
271             ViewTreeObserver observer = getViewTreeObserver();
272             observer.removeOnScrollChangedListener(mScrollChangedListener);
273             observer.removeOnPreDrawListener(mDrawListener);
274             mGlobalListenersAdded = false;
275         }
276 
277         mRequestedVisible = false;
278         updateWindow(false, false);
279         mHaveFrame = false;
280         if (mWindow != null) {
281             try {
282                 mSession.remove(mWindow);
283             } catch (RemoteException ex) {
284                 // Not much we can do here...
285             }
286             mWindow = null;
287         }
288         mSession = null;
289         mLayout.token = null;
290 
291         super.onDetachedFromWindow();
292     }
293 
294     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)295     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
296         int width = mRequestedWidth >= 0
297                 ? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0)
298                 : getDefaultSize(0, widthMeasureSpec);
299         int height = mRequestedHeight >= 0
300                 ? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0)
301                 : getDefaultSize(0, heightMeasureSpec);
302         setMeasuredDimension(width, height);
303     }
304 
305     /** @hide */
306     @Override
setFrame(int left, int top, int right, int bottom)307     protected boolean setFrame(int left, int top, int right, int bottom) {
308         boolean result = super.setFrame(left, top, right, bottom);
309         updateWindow(false, false);
310         return result;
311     }
312 
313     @Override
gatherTransparentRegion(Region region)314     public boolean gatherTransparentRegion(Region region) {
315         if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
316             return super.gatherTransparentRegion(region);
317         }
318 
319         boolean opaque = true;
320         if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
321             // this view draws, remove it from the transparent region
322             opaque = super.gatherTransparentRegion(region);
323         } else if (region != null) {
324             int w = getWidth();
325             int h = getHeight();
326             if (w>0 && h>0) {
327                 getLocationInWindow(mLocation);
328                 // otherwise, punch a hole in the whole hierarchy
329                 int l = mLocation[0];
330                 int t = mLocation[1];
331                 region.op(l, t, l+w, t+h, Region.Op.UNION);
332             }
333         }
334         if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
335             opaque = false;
336         }
337         return opaque;
338     }
339 
340     @Override
draw(Canvas canvas)341     public void draw(Canvas canvas) {
342         if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
343             // draw() is not called when SKIP_DRAW is set
344             if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
345                 // punch a whole in the view-hierarchy below us
346                 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
347             }
348         }
349         super.draw(canvas);
350     }
351 
352     @Override
dispatchDraw(Canvas canvas)353     protected void dispatchDraw(Canvas canvas) {
354         if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
355             // if SKIP_DRAW is cleared, draw() has already punched a hole
356             if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
357                 // punch a whole in the view-hierarchy below us
358                 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
359             }
360         }
361         super.dispatchDraw(canvas);
362     }
363 
364     /**
365      * Control whether the surface view's surface is placed on top of another
366      * regular surface view in the window (but still behind the window itself).
367      * This is typically used to place overlays on top of an underlying media
368      * surface view.
369      *
370      * <p>Note that this must be set before the surface view's containing
371      * window is attached to the window manager.
372      *
373      * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
374      */
setZOrderMediaOverlay(boolean isMediaOverlay)375     public void setZOrderMediaOverlay(boolean isMediaOverlay) {
376         mWindowType = isMediaOverlay
377                 ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
378                 : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
379     }
380 
381     /**
382      * Control whether the surface view's surface is placed on top of its
383      * window.  Normally it is placed behind the window, to allow it to
384      * (for the most part) appear to composite with the views in the
385      * hierarchy.  By setting this, you cause it to be placed above the
386      * window.  This means that none of the contents of the window this
387      * SurfaceView is in will be visible on top of its surface.
388      *
389      * <p>Note that this must be set before the surface view's containing
390      * window is attached to the window manager.
391      *
392      * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
393      */
setZOrderOnTop(boolean onTop)394     public void setZOrderOnTop(boolean onTop) {
395         if (onTop) {
396             mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
397             // ensures the surface is placed below the IME
398             mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
399         } else {
400             mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
401             mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
402         }
403     }
404 
405     /**
406      * Control whether the surface view's content should be treated as secure,
407      * preventing it from appearing in screenshots or from being viewed on
408      * non-secure displays.
409      *
410      * <p>Note that this must be set before the surface view's containing
411      * window is attached to the window manager.
412      *
413      * <p>See {@link android.view.Display#FLAG_SECURE} for details.
414      *
415      * @param isSecure True if the surface view is secure.
416      */
setSecure(boolean isSecure)417     public void setSecure(boolean isSecure) {
418         if (isSecure) {
419             mLayout.flags |= WindowManager.LayoutParams.FLAG_SECURE;
420         } else {
421             mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SECURE;
422         }
423     }
424 
425     /**
426      * Hack to allow special layering of windows.  The type is one of the
427      * types in WindowManager.LayoutParams.  This is a hack so:
428      * @hide
429      */
setWindowType(int type)430     public void setWindowType(int type) {
431         mWindowType = type;
432     }
433 
434     /** @hide */
updateWindow(boolean force, boolean redrawNeeded)435     protected void updateWindow(boolean force, boolean redrawNeeded) {
436         if (!mHaveFrame) {
437             return;
438         }
439         ViewRootImpl viewRoot = getViewRootImpl();
440         if (viewRoot != null) {
441             mTranslator = viewRoot.mTranslator;
442         }
443 
444         if (mTranslator != null) {
445             mSurface.setCompatibilityTranslator(mTranslator);
446         }
447 
448         int myWidth = mRequestedWidth;
449         if (myWidth <= 0) myWidth = getWidth();
450         int myHeight = mRequestedHeight;
451         if (myHeight <= 0) myHeight = getHeight();
452 
453         final boolean creating = mWindow == null;
454         final boolean formatChanged = mFormat != mRequestedFormat;
455         final boolean sizeChanged = mWindowSpaceWidth != myWidth || mWindowSpaceHeight != myHeight;
456         final boolean visibleChanged = mVisible != mRequestedVisible;
457         final boolean layoutSizeChanged = getWidth() != mLayout.width
458                 || getHeight() != mLayout.height;
459 
460         if (force || creating || formatChanged || sizeChanged || visibleChanged
461             || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
462             getLocationInWindow(mLocation);
463 
464             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
465                     + "Changes: creating=" + creating
466                     + " format=" + formatChanged + " size=" + sizeChanged
467                     + " visible=" + visibleChanged
468                     + " left=" + (mWindowSpaceLeft != mLocation[0])
469                     + " top=" + (mWindowSpaceTop != mLocation[1]));
470 
471             try {
472                 final boolean visible = mVisible = mRequestedVisible;
473                 mWindowSpaceLeft = mLocation[0];
474                 mWindowSpaceTop = mLocation[1];
475                 mWindowSpaceWidth = myWidth;
476                 mWindowSpaceHeight = myHeight;
477                 mFormat = mRequestedFormat;
478 
479                 // Scaling/Translate window's layout here because mLayout is not used elsewhere.
480 
481                 // Places the window relative
482                 mLayout.x = mWindowSpaceLeft;
483                 mLayout.y = mWindowSpaceTop;
484                 mLayout.width = getWidth();
485                 mLayout.height = getHeight();
486                 if (mTranslator != null) {
487                     mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
488                 }
489 
490                 mLayout.format = mRequestedFormat;
491                 mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
492                               | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
493                               | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
494                               | WindowManager.LayoutParams.FLAG_SCALED
495                               | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
496                               | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
497                               ;
498                 if (!creating && !force && !sizeChanged) {
499                     mLayout.privateFlags |=
500                             WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY;
501                 } else {
502                     mLayout.privateFlags &=
503                             ~WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY;
504                 }
505 
506                 if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
507                     mLayout.privateFlags |=
508                             WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
509                 }
510                 mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
511                     | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
512 
513                 if (mWindow == null) {
514                     Display display = getDisplay();
515                     mWindow = new MyWindow(this);
516                     mLayout.type = mWindowType;
517                     mLayout.gravity = Gravity.START|Gravity.TOP;
518                     mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
519                             mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets,
520                             mStableInsets);
521                 }
522 
523                 boolean realSizeChanged;
524                 boolean reportDrawNeeded;
525 
526                 int relayoutResult;
527 
528                 mSurfaceLock.lock();
529                 try {
530                     mUpdateWindowNeeded = false;
531                     reportDrawNeeded = mReportDrawNeeded;
532                     mReportDrawNeeded = false;
533                     mDrawingStopped = !visible;
534 
535                     if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
536                             + "Cur surface: " + mSurface);
537 
538                     relayoutResult = mSession.relayout(
539                         mWindow, mWindow.mSeq, mLayout, mWindowSpaceWidth, mWindowSpaceHeight,
540                             visible ? VISIBLE : GONE,
541                             WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
542                             mWinFrame, mOverscanInsets, mContentInsets,
543                             mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
544                             mConfiguration, mNewSurface);
545                     if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
546                         reportDrawNeeded = true;
547                     }
548 
549                     if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
550                             + "New surface: " + mNewSurface
551                             + ", vis=" + visible + ", frame=" + mWinFrame);
552 
553                     mSurfaceFrame.left = 0;
554                     mSurfaceFrame.top = 0;
555                     if (mTranslator == null) {
556                         mSurfaceFrame.right = mWinFrame.width();
557                         mSurfaceFrame.bottom = mWinFrame.height();
558                     } else {
559                         float appInvertedScale = mTranslator.applicationInvertedScale;
560                         mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
561                         mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
562                     }
563 
564                     final int surfaceWidth = mSurfaceFrame.right;
565                     final int surfaceHeight = mSurfaceFrame.bottom;
566                     realSizeChanged = mLastSurfaceWidth != surfaceWidth
567                             || mLastSurfaceHeight != surfaceHeight;
568                     mLastSurfaceWidth = surfaceWidth;
569                     mLastSurfaceHeight = surfaceHeight;
570                 } finally {
571                     mSurfaceLock.unlock();
572                 }
573 
574                 try {
575                     redrawNeeded |= creating | reportDrawNeeded;
576 
577                     SurfaceHolder.Callback callbacks[] = null;
578 
579                     final boolean surfaceChanged = (relayoutResult
580                             & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0;
581                     if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
582                         mSurfaceCreated = false;
583                         if (mSurface.isValid()) {
584                             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
585                                     + "visibleChanged -- surfaceDestroyed");
586                             callbacks = getSurfaceCallbacks();
587                             for (SurfaceHolder.Callback c : callbacks) {
588                                 c.surfaceDestroyed(mSurfaceHolder);
589                             }
590                             // Since Android N the same surface may be reused and given to us
591                             // again by the system server at a later point. However
592                             // as we didn't do this in previous releases, clients weren't
593                             // necessarily required to clean up properly in
594                             // surfaceDestroyed. This leads to problems for example when
595                             // clients don't destroy their EGL context, and try
596                             // and create a new one on the same surface following reuse.
597                             // Since there is no valid use of the surface in-between
598                             // surfaceDestroyed and surfaceCreated, we force a disconnect,
599                             // so the next connect will always work if we end up reusing
600                             // the surface.
601                             if (mSurface.isValid()) {
602                                 mSurface.forceScopedDisconnect();
603                             }
604                         }
605                     }
606 
607                     mSurface.transferFrom(mNewSurface);
608                     if (visible && mSurface.isValid()) {
609                         if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
610                             mSurfaceCreated = true;
611                             mIsCreating = true;
612                             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
613                                     + "visibleChanged -- surfaceCreated");
614                             if (callbacks == null) {
615                                 callbacks = getSurfaceCallbacks();
616                             }
617                             for (SurfaceHolder.Callback c : callbacks) {
618                                 c.surfaceCreated(mSurfaceHolder);
619                             }
620                         }
621                         if (creating || formatChanged || sizeChanged
622                                 || visibleChanged || realSizeChanged) {
623                             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
624                                     + "surfaceChanged -- format=" + mFormat
625                                     + " w=" + myWidth + " h=" + myHeight);
626                             if (callbacks == null) {
627                                 callbacks = getSurfaceCallbacks();
628                             }
629                             for (SurfaceHolder.Callback c : callbacks) {
630                                 c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
631                             }
632                         }
633                         if (redrawNeeded) {
634                             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
635                                     + "surfaceRedrawNeeded");
636                             if (callbacks == null) {
637                                 callbacks = getSurfaceCallbacks();
638                             }
639                             for (SurfaceHolder.Callback c : callbacks) {
640                                 if (c instanceof SurfaceHolder.Callback2) {
641                                     ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
642                                             mSurfaceHolder);
643                                 }
644                             }
645                         }
646                     }
647                 } finally {
648                     mIsCreating = false;
649                     if (redrawNeeded) {
650                         if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
651                                 + "finishedDrawing");
652                         mSession.finishDrawing(mWindow);
653                     }
654                     mSession.performDeferredDestroy(mWindow);
655                 }
656             } catch (RemoteException ex) {
657                 Log.e(TAG, "Exception from relayout", ex);
658             }
659             if (DEBUG) Log.v(
660                 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
661                 " w=" + mLayout.width + " h=" + mLayout.height +
662                 ", frame=" + mSurfaceFrame);
663         } else {
664             // Calculate the window position in case RT loses the window
665             // and we need to fallback to a UI-thread driven position update
666             getLocationInWindow(mLocation);
667             final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
668                     || mWindowSpaceTop != mLocation[1];
669             if (positionChanged || layoutSizeChanged) { // Only the position has changed
670                 mWindowSpaceLeft = mLocation[0];
671                 mWindowSpaceTop = mLocation[1];
672                 // For our size changed check, we keep mLayout.width and mLayout.height
673                 // in view local space.
674                 mLocation[0] = mLayout.width = getWidth();
675                 mLocation[1] = mLayout.height = getHeight();
676 
677                 transformFromViewToWindowSpace(mLocation);
678 
679                 mTmpRect.set(mWindowSpaceLeft, mWindowSpaceTop,
680                         mLocation[0], mLocation[1]);
681 
682                 if (mTranslator != null) {
683                     mTranslator.translateRectInAppWindowToScreen(mTmpRect);
684                 }
685 
686                 if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
687                     try {
688                         if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition UI, " +
689                                 "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
690                                 mTmpRect.left, mTmpRect.top,
691                                 mTmpRect.right, mTmpRect.bottom));
692                         mSession.repositionChild(mWindow, mTmpRect.left, mTmpRect.top,
693                                 mTmpRect.right, mTmpRect.bottom, -1, mTmpRect);
694                     } catch (RemoteException ex) {
695                         Log.e(TAG, "Exception from relayout", ex);
696                     }
697                 }
698             }
699         }
700     }
701 
702     private Rect mRTLastReportedPosition = new Rect();
703 
704     /**
705      * Called by native by a Rendering Worker thread to update the window position
706      * @hide
707      */
updateWindowPosition_renderWorker(long frameNumber, int left, int top, int right, int bottom)708     public final void updateWindowPosition_renderWorker(long frameNumber,
709             int left, int top, int right, int bottom) {
710         IWindowSession session = mSession;
711         MyWindow window = mWindow;
712         if (session == null || window == null) {
713             // Guess we got detached, that sucks
714             return;
715         }
716         // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
717         // its 2nd frame if RenderThread is running slowly could potentially see
718         // this as false, enter the branch, get pre-empted, then this comes along
719         // and reports a new position, then the UI thread resumes and reports
720         // its position. This could therefore be de-sync'd in that interval, but
721         // the synchronization would violate the rule that RT must never block
722         // on the UI thread which would open up potential deadlocks. The risk of
723         // a single-frame desync is therefore preferable for now.
724         mRtHandlingPositionUpdates = true;
725         if (mRTLastReportedPosition.left == left
726                 && mRTLastReportedPosition.top == top
727                 && mRTLastReportedPosition.right == right
728                 && mRTLastReportedPosition.bottom == bottom) {
729             return;
730         }
731         try {
732             if (DEBUG) {
733                 Log.d(TAG, String.format("%d updateWindowPosition RenderWorker, frameNr = %d, " +
734                         "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
735                         frameNumber, left, top, right, bottom));
736             }
737             // Just using mRTLastReportedPosition as a dummy rect here
738             session.repositionChild(window, left, top, right, bottom,
739                     frameNumber,
740                     mRTLastReportedPosition);
741             // Now overwrite mRTLastReportedPosition with our values
742             mRTLastReportedPosition.set(left, top, right, bottom);
743         } catch (RemoteException ex) {
744             Log.e(TAG, "Exception from repositionChild", ex);
745         }
746     }
747 
748     /**
749      * Called by native on RenderThread to notify that the window is no longer in the
750      * draw tree. UI thread is blocked at this point.
751      * @hide
752      */
windowPositionLost_uiRtSync(long frameNumber)753     public final void windowPositionLost_uiRtSync(long frameNumber) {
754         if (DEBUG) {
755             Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
756                     System.identityHashCode(this), frameNumber));
757         }
758         IWindowSession session = mSession;
759         MyWindow window = mWindow;
760         if (session == null || window == null) {
761             // We got detached prior to receiving this, abort
762             return;
763         }
764         if (mRtHandlingPositionUpdates) {
765             mRtHandlingPositionUpdates = false;
766             // This callback will happen while the UI thread is blocked, so we can
767             // safely access other member variables at this time.
768             // So do what the UI thread would have done if RT wasn't handling position
769             // updates.
770             mTmpRect.set(mLayout.x, mLayout.y,
771                     mLayout.x + mLayout.width,
772                     mLayout.y + mLayout.height);
773 
774             if (!mTmpRect.isEmpty() && !mTmpRect.equals(mRTLastReportedPosition)) {
775                 try {
776                     if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition, " +
777                             "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
778                             mTmpRect.left, mTmpRect.top,
779                             mTmpRect.right, mTmpRect.bottom));
780                     session.repositionChild(window, mTmpRect.left, mTmpRect.top,
781                             mTmpRect.right, mTmpRect.bottom, frameNumber, mWinFrame);
782                 } catch (RemoteException ex) {
783                     Log.e(TAG, "Exception from relayout", ex);
784                 }
785             }
786             mRTLastReportedPosition.setEmpty();
787         }
788     }
789 
getSurfaceCallbacks()790     private SurfaceHolder.Callback[] getSurfaceCallbacks() {
791         SurfaceHolder.Callback callbacks[];
792         synchronized (mCallbacks) {
793             callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
794             mCallbacks.toArray(callbacks);
795         }
796         return callbacks;
797     }
798 
handleGetNewSurface()799     void handleGetNewSurface() {
800         updateWindow(false, false);
801     }
802 
803     /**
804      * Check to see if the surface has fixed size dimensions or if the surface's
805      * dimensions are dimensions are dependent on its current layout.
806      *
807      * @return true if the surface has dimensions that are fixed in size
808      * @hide
809      */
isFixedSize()810     public boolean isFixedSize() {
811         return (mRequestedWidth != -1 || mRequestedHeight != -1);
812     }
813 
814     private static class MyWindow extends BaseIWindow {
815         private final WeakReference<SurfaceView> mSurfaceView;
816 
MyWindow(SurfaceView surfaceView)817         public MyWindow(SurfaceView surfaceView) {
818             mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
819         }
820 
821         @Override
resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig, Rect backDropRect, boolean forceLayout, boolean alwaysConsumeNavBar)822         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
823                 Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
824                 Configuration newConfig, Rect backDropRect, boolean forceLayout,
825                 boolean alwaysConsumeNavBar) {
826             SurfaceView surfaceView = mSurfaceView.get();
827             if (surfaceView != null) {
828                 if (DEBUG) Log.v(TAG, surfaceView + " got resized: w=" + frame.width()
829                         + " h=" + frame.height() + ", cur w=" + mCurWidth + " h=" + mCurHeight);
830                 surfaceView.mSurfaceLock.lock();
831                 try {
832                     if (reportDraw) {
833                         surfaceView.mUpdateWindowNeeded = true;
834                         surfaceView.mReportDrawNeeded = true;
835                         surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
836                     } else if (surfaceView.mWinFrame.width() != frame.width()
837                             || surfaceView.mWinFrame.height() != frame.height()
838                             || forceLayout) {
839                         surfaceView.mUpdateWindowNeeded = true;
840                         surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
841                     }
842                 } finally {
843                     surfaceView.mSurfaceLock.unlock();
844                 }
845             }
846         }
847 
848         @Override
dispatchAppVisibility(boolean visible)849         public void dispatchAppVisibility(boolean visible) {
850             // The point of SurfaceView is to let the app control the surface.
851         }
852 
853         @Override
dispatchGetNewSurface()854         public void dispatchGetNewSurface() {
855             SurfaceView surfaceView = mSurfaceView.get();
856             if (surfaceView != null) {
857                 Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
858                 surfaceView.mHandler.sendMessage(msg);
859             }
860         }
861 
862         @Override
windowFocusChanged(boolean hasFocus, boolean touchEnabled)863         public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
864             Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
865         }
866 
867         @Override
executeCommand(String command, String parameters, ParcelFileDescriptor out)868         public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
869         }
870 
871         int mCurWidth = -1;
872         int mCurHeight = -1;
873     }
874 
875     private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
876 
877         private static final String LOG_TAG = "SurfaceHolder";
878 
879         @Override
880         public boolean isCreating() {
881             return mIsCreating;
882         }
883 
884         @Override
885         public void addCallback(Callback callback) {
886             synchronized (mCallbacks) {
887                 // This is a linear search, but in practice we'll
888                 // have only a couple callbacks, so it doesn't matter.
889                 if (mCallbacks.contains(callback) == false) {
890                     mCallbacks.add(callback);
891                 }
892             }
893         }
894 
895         @Override
896         public void removeCallback(Callback callback) {
897             synchronized (mCallbacks) {
898                 mCallbacks.remove(callback);
899             }
900         }
901 
902         @Override
903         public void setFixedSize(int width, int height) {
904             if (mRequestedWidth != width || mRequestedHeight != height) {
905                 mRequestedWidth = width;
906                 mRequestedHeight = height;
907                 requestLayout();
908             }
909         }
910 
911         @Override
912         public void setSizeFromLayout() {
913             if (mRequestedWidth != -1 || mRequestedHeight != -1) {
914                 mRequestedWidth = mRequestedHeight = -1;
915                 requestLayout();
916             }
917         }
918 
919         @Override
920         public void setFormat(int format) {
921 
922             // for backward compatibility reason, OPAQUE always
923             // means 565 for SurfaceView
924             if (format == PixelFormat.OPAQUE)
925                 format = PixelFormat.RGB_565;
926 
927             mRequestedFormat = format;
928             if (mWindow != null) {
929                 updateWindow(false, false);
930             }
931         }
932 
933         /**
934          * @deprecated setType is now ignored.
935          */
936         @Override
937         @Deprecated
938         public void setType(int type) { }
939 
940         @Override
941         public void setKeepScreenOn(boolean screenOn) {
942             Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG);
943             msg.arg1 = screenOn ? 1 : 0;
944             mHandler.sendMessage(msg);
945         }
946 
947         /**
948          * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
949          *
950          * After drawing into the provided {@link Canvas}, the caller must
951          * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
952          *
953          * The caller must redraw the entire surface.
954          * @return A canvas for drawing into the surface.
955          */
956         @Override
957         public Canvas lockCanvas() {
958             return internalLockCanvas(null);
959         }
960 
961         /**
962          * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
963          *
964          * After drawing into the provided {@link Canvas}, the caller must
965          * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
966          *
967          * @param inOutDirty A rectangle that represents the dirty region that the caller wants
968          * to redraw.  This function may choose to expand the dirty rectangle if for example
969          * the surface has been resized or if the previous contents of the surface were
970          * not available.  The caller must redraw the entire dirty region as represented
971          * by the contents of the inOutDirty rectangle upon return from this function.
972          * The caller may also pass <code>null</code> instead, in the case where the
973          * entire surface should be redrawn.
974          * @return A canvas for drawing into the surface.
975          */
976         @Override
977         public Canvas lockCanvas(Rect inOutDirty) {
978             return internalLockCanvas(inOutDirty);
979         }
980 
981         private final Canvas internalLockCanvas(Rect dirty) {
982             mSurfaceLock.lock();
983 
984             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped="
985                     + mDrawingStopped + ", win=" + mWindow);
986 
987             Canvas c = null;
988             if (!mDrawingStopped && mWindow != null) {
989                 try {
990                     c = mSurface.lockCanvas(dirty);
991                 } catch (Exception e) {
992                     Log.e(LOG_TAG, "Exception locking surface", e);
993                 }
994             }
995 
996             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Returned canvas: " + c);
997             if (c != null) {
998                 mLastLockTime = SystemClock.uptimeMillis();
999                 return c;
1000             }
1001 
1002             // If the Surface is not ready to be drawn, then return null,
1003             // but throttle calls to this function so it isn't called more
1004             // than every 100ms.
1005             long now = SystemClock.uptimeMillis();
1006             long nextTime = mLastLockTime + 100;
1007             if (nextTime > now) {
1008                 try {
1009                     Thread.sleep(nextTime-now);
1010                 } catch (InterruptedException e) {
1011                 }
1012                 now = SystemClock.uptimeMillis();
1013             }
1014             mLastLockTime = now;
1015             mSurfaceLock.unlock();
1016 
1017             return null;
1018         }
1019 
1020         /**
1021          * Posts the new contents of the {@link Canvas} to the surface and
1022          * releases the {@link Canvas}.
1023          *
1024          * @param canvas The canvas previously obtained from {@link #lockCanvas}.
1025          */
1026         @Override
1027         public void unlockCanvasAndPost(Canvas canvas) {
1028             mSurface.unlockCanvasAndPost(canvas);
1029             mSurfaceLock.unlock();
1030         }
1031 
1032         @Override
1033         public Surface getSurface() {
1034             return mSurface;
1035         }
1036 
1037         @Override
1038         public Rect getSurfaceFrame() {
1039             return mSurfaceFrame;
1040         }
1041     };
1042 }
1043