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