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