OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / core / java / android / view / SurfaceView.java
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 }