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