Home | History | Annotate | Download | only in view
      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 
    180     public SurfaceView(Context context) {
    181         super(context);
    182         init();
    183     }
    184 
    185     public SurfaceView(Context context, AttributeSet attrs) {
    186         super(context, attrs);
    187         init();
    188     }
    189 
    190     public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
    191         super(context, attrs, defStyle);
    192         init();
    193     }
    194 
    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      */
    205     public SurfaceHolder getHolder() {
    206         return mSurfaceHolder;
    207     }
    208 
    209     @Override
    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
    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
    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
    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
    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
    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
    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
    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
    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      */
    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      */
    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      */
    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      */
    414     public void setWindowType(int type) {
    415         mWindowType = type;
    416     }
    417 
    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 
    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 
    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      */
    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 
    640         public MyWindow(SurfaceView surfaceView) {
    641             mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
    642         }
    643 
    644         @Override
    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 
    669         public void dispatchAppVisibility(boolean visible) {
    670             // The point of SurfaceView is to let the app control the surface.
    671         }
    672 
    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 
    681         public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
    682             Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
    683         }
    684 
    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