2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 import com.android.internal.view.BaseIWindow;
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;
37 import java.lang.ref.WeakReference;
38 import java.util.ArrayList;
39 import java.util.concurrent.locks.ReentrantLock;
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
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.
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.
61 * <p>Access to the underlying surface is provided via the SurfaceHolder interface,
62 * which can be retrieved by calling {@link #getHolder}.
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.
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:
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()}
83 * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
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>
93 public class SurfaceView extends View {
94 static private final String TAG = "SurfaceView";
95 static private final boolean DEBUG = false;
97 final ArrayList<SurfaceHolder.Callback> mCallbacks
98 = new ArrayList<SurfaceHolder.Callback>();
100 final int[] mLocation = new int[2];
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;
107 final WindowManager.LayoutParams mLayout
108 = new WindowManager.LayoutParams();
109 IWindowSession mSession;
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();
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;
124 int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
126 boolean mIsCreating = false;
127 private volatile boolean mRtHandlingPositionUpdates = false;
129 final Handler mHandler = new Handler() {
131 public void handleMessage(Message msg) {
133 case KEEP_SCREEN_ON_MSG: {
134 setKeepScreenOn(msg.arg1 != 0);
136 case GET_NEW_SURFACE_MSG: {
137 handleGetNewSurface();
139 case UPDATE_WINDOW_MSG: {
140 updateWindow(false, false);
146 private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
147 = new ViewTreeObserver.OnScrollChangedListener() {
149 public void onScrollChanged() {
150 updateWindow(false, false);
154 private final ViewTreeObserver.OnPreDrawListener mDrawListener =
155 new ViewTreeObserver.OnPreDrawListener() {
157 public boolean onPreDraw() {
158 // reposition ourselves where the surface is
159 mHaveFrame = getWidth() > 0 && getHeight() > 0;
160 updateWindow(false, false);
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.
173 int mRequestedFormat = PixelFormat.RGB_565;
175 boolean mHaveFrame = false;
176 boolean mSurfaceCreated = false;
177 long mLastLockTime = 0;
179 boolean mVisible = false;
180 int mWindowSpaceLeft = -1;
181 int mWindowSpaceTop = -1;
182 int mWindowSpaceWidth = -1;
183 int mWindowSpaceHeight = -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;
193 private boolean mGlobalListenersAdded;
195 public SurfaceView(Context context) {
200 public SurfaceView(Context context, AttributeSet attrs) {
201 super(context, attrs);
205 public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
206 super(context, attrs, defStyleAttr);
210 public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
211 super(context, attrs, defStyleAttr, defStyleRes);
215 private void init() {
216 setWillNotDraw(true);
220 * Return the SurfaceHolder providing access and control over this
221 * SurfaceView's underlying surface.
223 * @return SurfaceHolder The holder of the surface.
225 public SurfaceHolder getHolder() {
226 return mSurfaceHolder;
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;
238 if (!mGlobalListenersAdded) {
239 ViewTreeObserver observer = getViewTreeObserver();
240 observer.addOnScrollChangedListener(mScrollChangedListener);
241 observer.addOnPreDrawListener(mDrawListener);
242 mGlobalListenersAdded = true;
247 protected void onWindowVisibilityChanged(int visibility) {
248 super.onWindowVisibilityChanged(visibility);
249 mWindowVisibility = visibility == VISIBLE;
250 mRequestedVisible = mWindowVisibility && mViewVisibility;
251 updateWindow(false, false);
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.
268 mRequestedVisible = newRequestedVisible;
269 updateWindow(false, false);
273 protected void onDetachedFromWindow() {
274 if (mGlobalListenersAdded) {
275 ViewTreeObserver observer = getViewTreeObserver();
276 observer.removeOnScrollChangedListener(mScrollChangedListener);
277 observer.removeOnPreDrawListener(mDrawListener);
278 mGlobalListenersAdded = false;
281 mRequestedVisible = false;
282 updateWindow(false, false);
284 if (mWindow != null) {
286 mSession.remove(mWindow);
287 } catch (RemoteException ex) {
288 // Not much we can do here...
293 mLayout.token = null;
295 super.onDetachedFromWindow();
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);
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);
318 public boolean gatherTransparentRegion(Region region) {
319 if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
320 return super.gatherTransparentRegion(region);
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) {
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);
338 if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
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);
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);
365 super.dispatchDraw(canvas);
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
374 * <p>Note that this must be set before the surface view's containing
375 * window is attached to the window manager.
377 * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
379 public void setZOrderMediaOverlay(boolean isMediaOverlay) {
380 mWindowType = isMediaOverlay
381 ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
382 : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
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.
393 * <p>Note that this must be set before the surface view's containing
394 * window is attached to the window manager.
396 * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
398 public void setZOrderOnTop(boolean 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;
404 mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
405 mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
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.
414 * <p>Note that this must be set before the surface view's containing
415 * window is attached to the window manager.
417 * <p>See {@link android.view.Display#FLAG_SECURE} for details.
419 * @param isSecure True if the surface view is secure.
421 public void setSecure(boolean isSecure) {
423 mLayout.flags |= WindowManager.LayoutParams.FLAG_SECURE;
425 mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SECURE;
430 * Hack to allow special layering of windows. The type is one of the
431 * types in WindowManager.LayoutParams. This is a hack so:
434 public void setWindowType(int type) {
439 protected void updateWindow(boolean force, boolean redrawNeeded) {
443 ViewRootImpl viewRoot = getViewRootImpl();
444 if (viewRoot != null) {
445 mTranslator = viewRoot.mTranslator;
448 if (mTranslator != null) {
449 mSurface.setCompatibilityTranslator(mTranslator);
452 int myWidth = mRequestedWidth;
453 if (myWidth <= 0) myWidth = getWidth();
454 int myHeight = mRequestedHeight;
455 if (myHeight <= 0) myHeight = getHeight();
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;
464 if (force || creating || formatChanged || sizeChanged || visibleChanged
465 || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
466 getLocationInWindow(mLocation);
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]));
476 final boolean visible = mVisible = mRequestedVisible;
477 mWindowSpaceLeft = mLocation[0];
478 mWindowSpaceTop = mLocation[1];
479 mWindowSpaceWidth = myWidth;
480 mWindowSpaceHeight = myHeight;
481 mFormat = mRequestedFormat;
483 // Scaling/Translate window's layout here because mLayout is not used elsewhere.
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);
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
502 if (!creating && !force && !mUpdateWindowNeeded && !sizeChanged) {
503 mLayout.privateFlags |=
504 WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY;
506 mLayout.privateFlags &=
507 ~WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY;
510 if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
511 mLayout.privateFlags |=
512 WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
514 mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
515 | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
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,
527 boolean realSizeChanged;
528 boolean reportDrawNeeded;
534 mUpdateWindowNeeded = false;
535 reportDrawNeeded = mReportDrawNeeded;
536 mReportDrawNeeded = false;
537 mDrawingStopped = !visible;
539 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
540 + "Cur surface: " + mSurface);
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;
553 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
554 + "New surface: " + mNewSurface
555 + ", vis=" + visible + ", frame=" + mWinFrame);
557 mSurfaceFrame.left = 0;
558 mSurfaceFrame.top = 0;
559 if (mTranslator == null) {
560 mSurfaceFrame.right = mWinFrame.width();
561 mSurfaceFrame.bottom = mWinFrame.height();
563 float appInvertedScale = mTranslator.applicationInvertedScale;
564 mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
565 mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
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;
575 mSurfaceLock.unlock();
579 redrawNeeded |= creating | reportDrawNeeded;
581 SurfaceHolder.Callback callbacks[] = null;
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);
597 mSurface.transferFrom(mNewSurface);
598 if (visible && mSurface.isValid()) {
599 if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
600 mSurfaceCreated = true;
602 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
603 + "visibleChanged -- surfaceCreated");
604 if (callbacks == null) {
605 callbacks = getSurfaceCallbacks();
607 for (SurfaceHolder.Callback c : callbacks) {
608 c.surfaceCreated(mSurfaceHolder);
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();
619 for (SurfaceHolder.Callback c : callbacks) {
620 c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
624 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
625 + "surfaceRedrawNeeded");
626 if (callbacks == null) {
627 callbacks = getSurfaceCallbacks();
629 for (SurfaceHolder.Callback c : callbacks) {
630 if (c instanceof SurfaceHolder.Callback2) {
631 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
640 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
641 + "finishedDrawing");
642 mSession.finishDrawing(mWindow);
644 mSession.performDeferredDestroy(mWindow);
646 } catch (RemoteException ex) {
647 Log.e(TAG, "Exception from relayout", ex);
650 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
651 " w=" + mLayout.width + " h=" + mLayout.height +
652 ", frame=" + mSurfaceFrame);
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();
667 transformFromViewToWindowSpace(mLocation);
669 mWinFrame.set(mWindowSpaceLeft, mWindowSpaceTop,
670 mLocation[0], mLocation[1]);
672 if (mTranslator != null) {
673 mTranslator.translateRectInAppWindowToScreen(mWinFrame);
676 if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
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);
692 private Rect mRTLastReportedPosition = new Rect();
695 * Called by native on RenderThread to update the window position
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
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) {
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));
727 // Just using mRTLastReportedPosition as a dummy rect here
728 session.repositionChild(window, left, top, right, bottom,
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);
739 * Called by native on RenderThread to notify that the window is no longer in the
743 public final void windowPositionLostRT(long frameNumber) {
745 Log.d(TAG, String.format("%d windowPositionLostRT RT, frameNr = %d",
746 System.identityHashCode(this), frameNumber));
748 IWindowSession session = mSession;
749 MyWindow window = mWindow;
750 if (session == null || window == null) {
751 // We got detached prior to receiving this, abort
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
760 if (!mWinFrame.isEmpty() && !mWinFrame.equals(mRTLastReportedPosition)) {
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);
772 mRTLastReportedPosition.setEmpty();
776 private SurfaceHolder.Callback[] getSurfaceCallbacks() {
777 SurfaceHolder.Callback callbacks[];
778 synchronized (mCallbacks) {
779 callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
780 mCallbacks.toArray(callbacks);
785 void handleGetNewSurface() {
786 updateWindow(false, false);
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.
793 * @return true if the surface has dimensions that are fixed in size
796 public boolean isFixedSize() {
797 return (mRequestedWidth != -1 || mRequestedHeight != -1);
800 private static class MyWindow extends BaseIWindow {
801 private final WeakReference<SurfaceView> mSurfaceView;
803 public MyWindow(SurfaceView surfaceView) {
804 mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
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();
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()
825 surfaceView.mUpdateWindowNeeded = true;
826 surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
829 surfaceView.mSurfaceLock.unlock();
835 public void dispatchAppVisibility(boolean visible) {
836 // The point of SurfaceView is to let the app control the surface.
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);
849 public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
850 Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
854 public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
861 private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
863 private static final String LOG_TAG = "SurfaceHolder";
866 public boolean isCreating() {
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);
882 public void removeCallback(Callback callback) {
883 synchronized (mCallbacks) {
884 mCallbacks.remove(callback);
889 public void setFixedSize(int width, int height) {
890 if (mRequestedWidth != width || mRequestedHeight != height) {
891 mRequestedWidth = width;
892 mRequestedHeight = height;
898 public void setSizeFromLayout() {
899 if (mRequestedWidth != -1 || mRequestedHeight != -1) {
900 mRequestedWidth = mRequestedHeight = -1;
906 public void setFormat(int format) {
908 // for backward compatibility reason, OPAQUE always
909 // means 565 for SurfaceView
910 if (format == PixelFormat.OPAQUE)
911 format = PixelFormat.RGB_565;
913 mRequestedFormat = format;
914 if (mWindow != null) {
915 updateWindow(false, false);
920 * @deprecated setType is now ignored.
924 public void setType(int type) { }
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);
934 * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
936 * After drawing into the provided {@link Canvas}, the caller must
937 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
939 * The caller must redraw the entire surface.
940 * @return A canvas for drawing into the surface.
943 public Canvas lockCanvas() {
944 return internalLockCanvas(null);
948 * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
950 * After drawing into the provided {@link Canvas}, the caller must
951 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
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.
963 public Canvas lockCanvas(Rect inOutDirty) {
964 return internalLockCanvas(inOutDirty);
967 private final Canvas internalLockCanvas(Rect dirty) {
970 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped="
971 + mDrawingStopped + ", win=" + mWindow);
974 if (!mDrawingStopped && mWindow != null) {
976 c = mSurface.lockCanvas(dirty);
977 } catch (Exception e) {
978 Log.e(LOG_TAG, "Exception locking surface", e);
982 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Returned canvas: " + c);
984 mLastLockTime = SystemClock.uptimeMillis();
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
991 long now = SystemClock.uptimeMillis();
992 long nextTime = mLastLockTime + 100;
993 if (nextTime > now) {
995 Thread.sleep(nextTime-now);
996 } catch (InterruptedException e) {
998 now = SystemClock.uptimeMillis();
1000 mLastLockTime = now;
1001 mSurfaceLock.unlock();
1007 * Posts the new contents of the {@link Canvas} to the surface and
1008 * releases the {@link Canvas}.
1010 * @param canvas The canvas previously obtained from {@link #lockCanvas}.
1013 public void unlockCanvasAndPost(Canvas canvas) {
1014 mSurface.unlockCanvasAndPost(canvas);
1015 mSurfaceLock.unlock();
1019 public Surface getSurface() {
1024 public Rect getSurfaceFrame() {
1025 return mSurfaceFrame;