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