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 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 
    181     public SurfaceView(Context context) {
    182         super(context);
    183         init();
    184     }
    185 
    186     public SurfaceView(Context context, AttributeSet attrs) {
    187         super(context, attrs);
    188         init();
    189     }
    190 
    191     public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
    192         super(context, attrs, defStyle);
    193         init();
    194     }
    195 
    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      */
    206     public SurfaceHolder getHolder() {
    207         return mSurfaceHolder;
    208     }
    209 
    210     @Override
    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
    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
    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
    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
    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
    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
    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
    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
    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      */
    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      */
    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      */
    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      */
    415     public void setWindowType(int type) {
    416         mWindowType = type;
    417     }
    418 
    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 
    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 
    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      */
    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 
    643         public MyWindow(SurfaceView surfaceView) {
    644             mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
    645         }
    646 
    647         @Override
    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
    673         public void dispatchAppVisibility(boolean visible) {
    674             // The point of SurfaceView is to let the app control the surface.
    675         }
    676 
    677         @Override
    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
    687         public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
    688             Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
    689         }
    690 
    691         @Override
    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