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