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