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 static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_OVERLAY_SUBLAYER; 20 import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_SUBLAYER; 21 import static android.view.WindowManagerPolicyConstants.APPLICATION_PANEL_SUBLAYER; 22 23 import android.annotation.UnsupportedAppUsage; 24 import android.content.Context; 25 import android.content.res.CompatibilityInfo.Translator; 26 import android.content.res.Configuration; 27 import android.graphics.BlendMode; 28 import android.graphics.Canvas; 29 import android.graphics.Color; 30 import android.graphics.Paint; 31 import android.graphics.PixelFormat; 32 import android.graphics.PorterDuff; 33 import android.graphics.Rect; 34 import android.graphics.Region; 35 import android.graphics.RenderNode; 36 import android.os.Build; 37 import android.os.Handler; 38 import android.os.Looper; 39 import android.os.SystemClock; 40 import android.util.AttributeSet; 41 import android.util.Log; 42 43 import com.android.internal.view.SurfaceCallbackHelper; 44 45 import java.util.ArrayList; 46 import java.util.concurrent.locks.ReentrantLock; 47 48 /** 49 * Provides a dedicated drawing surface embedded inside of a view hierarchy. 50 * You can control the format of this surface and, if you like, its size; the 51 * SurfaceView takes care of placing the surface at the correct location on the 52 * screen 53 * 54 * <p>The surface is Z ordered so that it is behind the window holding its 55 * SurfaceView; the SurfaceView punches a hole in its window to allow its 56 * surface to be displayed. The view hierarchy will take care of correctly 57 * compositing with the Surface any siblings of the SurfaceView that would 58 * normally appear on top of it. This can be used to place overlays such as 59 * buttons on top of the Surface, though note however that it can have an 60 * impact on performance since a full alpha-blended composite will be performed 61 * each time the Surface changes. 62 * 63 * <p> The transparent region that makes the surface visible is based on the 64 * layout positions in the view hierarchy. If the post-layout transform 65 * properties are used to draw a sibling view on top of the SurfaceView, the 66 * view may not be properly composited with the surface. 67 * 68 * <p>Access to the underlying surface is provided via the SurfaceHolder interface, 69 * which can be retrieved by calling {@link #getHolder}. 70 * 71 * <p>The Surface will be created for you while the SurfaceView's window is 72 * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated} 73 * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the 74 * Surface is created and destroyed as the window is shown and hidden. 75 * 76 * <p>One of the purposes of this class is to provide a surface in which a 77 * secondary thread can render into the screen. If you are going to use it 78 * this way, you need to be aware of some threading semantics: 79 * 80 * <ul> 81 * <li> All SurfaceView and 82 * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called 83 * from the thread running the SurfaceView's window (typically the main thread 84 * of the application). They thus need to correctly synchronize with any 85 * state that is also touched by the drawing thread. 86 * <li> You must ensure that the drawing thread only touches the underlying 87 * Surface while it is valid -- between 88 * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()} 89 * and 90 * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}. 91 * </ul> 92 * 93 * <p class="note"><strong>Note:</strong> Starting in platform version 94 * {@link android.os.Build.VERSION_CODES#N}, SurfaceView's window position is 95 * updated synchronously with other View rendering. This means that translating 96 * and scaling a SurfaceView on screen will not cause rendering artifacts. Such 97 * artifacts may occur on previous versions of the platform when its window is 98 * positioned asynchronously.</p> 99 */ 100 public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallback { 101 private static final String TAG = "SurfaceView"; 102 private static final boolean DEBUG = false; 103 104 @UnsupportedAppUsage 105 final ArrayList<SurfaceHolder.Callback> mCallbacks 106 = new ArrayList<SurfaceHolder.Callback>(); 107 108 final int[] mLocation = new int[2]; 109 110 @UnsupportedAppUsage 111 final ReentrantLock mSurfaceLock = new ReentrantLock(); 112 @UnsupportedAppUsage 113 final Surface mSurface = new Surface(); // Current surface in use 114 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 115 boolean mDrawingStopped = true; 116 // We use this to track if the application has produced a frame 117 // in to the Surface. Up until that point, we should be careful not to punch 118 // holes. 119 boolean mDrawFinished = false; 120 121 final Rect mScreenRect = new Rect(); 122 SurfaceSession mSurfaceSession; 123 124 SurfaceControl mSurfaceControl; 125 // In the case of format changes we switch out the surface in-place 126 // we need to preserve the old one until the new one has drawn. 127 SurfaceControl mDeferredDestroySurfaceControl; 128 SurfaceControl mBackgroundControl; 129 final Rect mTmpRect = new Rect(); 130 final Configuration mConfiguration = new Configuration(); 131 132 Paint mRoundedViewportPaint; 133 134 int mSubLayer = APPLICATION_MEDIA_SUBLAYER; 135 136 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 137 boolean mIsCreating = false; 138 private volatile boolean mRtHandlingPositionUpdates = false; 139 140 private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener 141 = new ViewTreeObserver.OnScrollChangedListener() { 142 @Override 143 public void onScrollChanged() { 144 updateSurface(); 145 } 146 }; 147 148 @UnsupportedAppUsage 149 private final ViewTreeObserver.OnPreDrawListener mDrawListener = 150 new ViewTreeObserver.OnPreDrawListener() { 151 @Override 152 public boolean onPreDraw() { 153 // reposition ourselves where the surface is 154 mHaveFrame = getWidth() > 0 && getHeight() > 0; 155 updateSurface(); 156 return true; 157 } 158 }; 159 160 boolean mRequestedVisible = false; 161 boolean mWindowVisibility = false; 162 boolean mLastWindowVisibility = false; 163 boolean mViewVisibility = false; 164 boolean mWindowStopped = false; 165 166 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 167 int mRequestedWidth = -1; 168 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 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 @UnsupportedAppUsage 174 int mRequestedFormat = PixelFormat.RGB_565; 175 176 @UnsupportedAppUsage 177 boolean mHaveFrame = false; 178 boolean mSurfaceCreated = false; 179 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 180 long mLastLockTime = 0; 181 182 boolean mVisible = false; 183 int mWindowSpaceLeft = -1; 184 int mWindowSpaceTop = -1; 185 int mSurfaceWidth = -1; 186 int mSurfaceHeight = -1; 187 float mCornerRadius; 188 @UnsupportedAppUsage 189 int mFormat = -1; 190 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 191 final Rect mSurfaceFrame = new Rect(); 192 int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1; 193 private Translator mTranslator; 194 195 private boolean mGlobalListenersAdded; 196 private boolean mAttachedToWindow; 197 198 private int mSurfaceFlags = SurfaceControl.HIDDEN; 199 200 private int mPendingReportDraws; 201 202 private SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction(); 203 SurfaceView(Context context)204 public SurfaceView(Context context) { 205 this(context, null); 206 } 207 SurfaceView(Context context, AttributeSet attrs)208 public SurfaceView(Context context, AttributeSet attrs) { 209 this(context, attrs, 0); 210 } 211 SurfaceView(Context context, AttributeSet attrs, int defStyleAttr)212 public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { 213 this(context, attrs, defStyleAttr, 0); 214 } 215 SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)216 public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 217 super(context, attrs, defStyleAttr, defStyleRes); 218 mRenderNode.addPositionUpdateListener(mPositionListener); 219 220 setWillNotDraw(true); 221 } 222 223 /** 224 * Return the SurfaceHolder providing access and control over this 225 * SurfaceView's underlying surface. 226 * 227 * @return SurfaceHolder The holder of the surface. 228 */ getHolder()229 public SurfaceHolder getHolder() { 230 return mSurfaceHolder; 231 } 232 updateRequestedVisibility()233 private void updateRequestedVisibility() { 234 mRequestedVisible = mViewVisibility && mWindowVisibility && !mWindowStopped; 235 } 236 237 /** @hide */ 238 @Override windowStopped(boolean stopped)239 public void windowStopped(boolean stopped) { 240 mWindowStopped = stopped; 241 updateRequestedVisibility(); 242 updateSurface(); 243 } 244 245 @Override onAttachedToWindow()246 protected void onAttachedToWindow() { 247 super.onAttachedToWindow(); 248 249 getViewRootImpl().addWindowStoppedCallback(this); 250 mWindowStopped = false; 251 252 mViewVisibility = getVisibility() == VISIBLE; 253 updateRequestedVisibility(); 254 255 mAttachedToWindow = true; 256 mParent.requestTransparentRegion(SurfaceView.this); 257 if (!mGlobalListenersAdded) { 258 ViewTreeObserver observer = getViewTreeObserver(); 259 observer.addOnScrollChangedListener(mScrollChangedListener); 260 observer.addOnPreDrawListener(mDrawListener); 261 mGlobalListenersAdded = true; 262 } 263 } 264 265 @Override onWindowVisibilityChanged(int visibility)266 protected void onWindowVisibilityChanged(int visibility) { 267 super.onWindowVisibilityChanged(visibility); 268 mWindowVisibility = visibility == VISIBLE; 269 updateRequestedVisibility(); 270 updateSurface(); 271 } 272 273 @Override setVisibility(int visibility)274 public void setVisibility(int visibility) { 275 super.setVisibility(visibility); 276 mViewVisibility = visibility == VISIBLE; 277 boolean newRequestedVisible = mWindowVisibility && mViewVisibility && !mWindowStopped; 278 if (newRequestedVisible != mRequestedVisible) { 279 // our base class (View) invalidates the layout only when 280 // we go from/to the GONE state. However, SurfaceView needs 281 // to request a re-layout when the visibility changes at all. 282 // This is needed because the transparent region is computed 283 // as part of the layout phase, and it changes (obviously) when 284 // the visibility changes. 285 requestLayout(); 286 } 287 mRequestedVisible = newRequestedVisible; 288 updateSurface(); 289 } 290 performDrawFinished()291 private void performDrawFinished() { 292 if (mPendingReportDraws > 0) { 293 mDrawFinished = true; 294 if (mAttachedToWindow) { 295 notifyDrawFinished(); 296 invalidate(); 297 } 298 } else { 299 Log.e(TAG, System.identityHashCode(this) + "finished drawing" 300 + " but no pending report draw (extra call" 301 + " to draw completion runnable?)"); 302 } 303 } 304 notifyDrawFinished()305 void notifyDrawFinished() { 306 ViewRootImpl viewRoot = getViewRootImpl(); 307 if (viewRoot != null) { 308 viewRoot.pendingDrawFinished(); 309 } 310 mPendingReportDraws--; 311 } 312 313 @Override onDetachedFromWindow()314 protected void onDetachedFromWindow() { 315 ViewRootImpl viewRoot = getViewRootImpl(); 316 // It's possible to create a SurfaceView using the default constructor and never 317 // attach it to a view hierarchy, this is a common use case when dealing with 318 // OpenGL. A developer will probably create a new GLSurfaceView, and let it manage 319 // the lifecycle. Instead of attaching it to a view, he/she can just pass 320 // the SurfaceHolder forward, most live wallpapers do it. 321 if (viewRoot != null) { 322 viewRoot.removeWindowStoppedCallback(this); 323 } 324 325 mAttachedToWindow = false; 326 if (mGlobalListenersAdded) { 327 ViewTreeObserver observer = getViewTreeObserver(); 328 observer.removeOnScrollChangedListener(mScrollChangedListener); 329 observer.removeOnPreDrawListener(mDrawListener); 330 mGlobalListenersAdded = false; 331 } 332 333 while (mPendingReportDraws > 0) { 334 notifyDrawFinished(); 335 } 336 337 mRequestedVisible = false; 338 339 updateSurface(); 340 if (mSurfaceControl != null) { 341 mSurfaceControl.remove(); 342 } 343 mSurfaceControl = null; 344 345 mHaveFrame = false; 346 347 super.onDetachedFromWindow(); 348 } 349 350 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)351 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 352 int width = mRequestedWidth >= 0 353 ? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0) 354 : getDefaultSize(0, widthMeasureSpec); 355 int height = mRequestedHeight >= 0 356 ? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0) 357 : getDefaultSize(0, heightMeasureSpec); 358 setMeasuredDimension(width, height); 359 } 360 361 /** @hide */ 362 @Override 363 @UnsupportedAppUsage setFrame(int left, int top, int right, int bottom)364 protected boolean setFrame(int left, int top, int right, int bottom) { 365 boolean result = super.setFrame(left, top, right, bottom); 366 updateSurface(); 367 return result; 368 } 369 370 @Override gatherTransparentRegion(Region region)371 public boolean gatherTransparentRegion(Region region) { 372 if (isAboveParent() || !mDrawFinished) { 373 return super.gatherTransparentRegion(region); 374 } 375 376 boolean opaque = true; 377 if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) { 378 // this view draws, remove it from the transparent region 379 opaque = super.gatherTransparentRegion(region); 380 } else if (region != null) { 381 int w = getWidth(); 382 int h = getHeight(); 383 if (w>0 && h>0) { 384 getLocationInWindow(mLocation); 385 // otherwise, punch a hole in the whole hierarchy 386 int l = mLocation[0]; 387 int t = mLocation[1]; 388 region.op(l, t, l+w, t+h, Region.Op.UNION); 389 } 390 } 391 if (PixelFormat.formatHasAlpha(mRequestedFormat)) { 392 opaque = false; 393 } 394 return opaque; 395 } 396 397 @Override draw(Canvas canvas)398 public void draw(Canvas canvas) { 399 if (mDrawFinished && !isAboveParent()) { 400 // draw() is not called when SKIP_DRAW is set 401 if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) { 402 // punch a whole in the view-hierarchy below us 403 clearSurfaceViewPort(canvas); 404 } 405 } 406 super.draw(canvas); 407 } 408 409 @Override dispatchDraw(Canvas canvas)410 protected void dispatchDraw(Canvas canvas) { 411 if (mDrawFinished && !isAboveParent()) { 412 // draw() is not called when SKIP_DRAW is set 413 if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { 414 // punch a whole in the view-hierarchy below us 415 clearSurfaceViewPort(canvas); 416 } 417 } 418 super.dispatchDraw(canvas); 419 } 420 clearSurfaceViewPort(Canvas canvas)421 private void clearSurfaceViewPort(Canvas canvas) { 422 if (mCornerRadius > 0f) { 423 canvas.getClipBounds(mTmpRect); 424 canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom, 425 mCornerRadius, mCornerRadius, mRoundedViewportPaint); 426 } else { 427 canvas.drawColor(0, PorterDuff.Mode.CLEAR); 428 } 429 } 430 431 /** 432 * Sets the corner radius for the SurfaceView. This will round both the corners of the 433 * underlying surface, as well as the corners of the hole created to expose the surface. 434 * 435 * @param cornerRadius the new radius of the corners in pixels 436 * @hide 437 */ setCornerRadius(float cornerRadius)438 public void setCornerRadius(float cornerRadius) { 439 mCornerRadius = cornerRadius; 440 if (mCornerRadius > 0f && mRoundedViewportPaint == null) { 441 mRoundedViewportPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 442 mRoundedViewportPaint.setBlendMode(BlendMode.CLEAR); 443 mRoundedViewportPaint.setColor(0); 444 } 445 invalidate(); 446 } 447 448 /** 449 * Control whether the surface view's surface is placed on top of another 450 * regular surface view in the window (but still behind the window itself). 451 * This is typically used to place overlays on top of an underlying media 452 * surface view. 453 * 454 * <p>Note that this must be set before the surface view's containing 455 * window is attached to the window manager. 456 * 457 * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}. 458 */ setZOrderMediaOverlay(boolean isMediaOverlay)459 public void setZOrderMediaOverlay(boolean isMediaOverlay) { 460 mSubLayer = isMediaOverlay 461 ? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER; 462 } 463 464 /** 465 * Control whether the surface view's surface is placed on top of its 466 * window. Normally it is placed behind the window, to allow it to 467 * (for the most part) appear to composite with the views in the 468 * hierarchy. By setting this, you cause it to be placed above the 469 * window. This means that none of the contents of the window this 470 * SurfaceView is in will be visible on top of its surface. 471 * 472 * <p>Note that this must be set before the surface view's containing 473 * window is attached to the window manager. 474 * 475 * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}. 476 */ setZOrderOnTop(boolean onTop)477 public void setZOrderOnTop(boolean onTop) { 478 if (onTop) { 479 mSubLayer = APPLICATION_PANEL_SUBLAYER; 480 } else { 481 mSubLayer = APPLICATION_MEDIA_SUBLAYER; 482 } 483 } 484 485 /** 486 * Control whether the surface view's content should be treated as secure, 487 * preventing it from appearing in screenshots or from being viewed on 488 * non-secure displays. 489 * 490 * <p>Note that this must be set before the surface view's containing 491 * window is attached to the window manager. 492 * 493 * <p>See {@link android.view.Display#FLAG_SECURE} for details. 494 * 495 * @param isSecure True if the surface view is secure. 496 */ setSecure(boolean isSecure)497 public void setSecure(boolean isSecure) { 498 if (isSecure) { 499 mSurfaceFlags |= SurfaceControl.SECURE; 500 } else { 501 mSurfaceFlags &= ~SurfaceControl.SECURE; 502 } 503 } 504 updateOpaqueFlag()505 private void updateOpaqueFlag() { 506 if (!PixelFormat.formatHasAlpha(mRequestedFormat)) { 507 mSurfaceFlags |= SurfaceControl.OPAQUE; 508 } else { 509 mSurfaceFlags &= ~SurfaceControl.OPAQUE; 510 } 511 } 512 getParentSurfaceInsets()513 private Rect getParentSurfaceInsets() { 514 final ViewRootImpl root = getViewRootImpl(); 515 if (root == null) { 516 return null; 517 } else { 518 return root.mWindowAttributes.surfaceInsets; 519 } 520 } 521 updateBackgroundVisibilityInTransaction(SurfaceControl viewRoot)522 private void updateBackgroundVisibilityInTransaction(SurfaceControl viewRoot) { 523 if (mBackgroundControl == null) { 524 return; 525 } 526 if ((mSubLayer < 0) && ((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)) { 527 mBackgroundControl.show(); 528 mBackgroundControl.setRelativeLayer(viewRoot, Integer.MIN_VALUE); 529 } else { 530 mBackgroundControl.hide(); 531 } 532 } 533 releaseSurfaces()534 private void releaseSurfaces() { 535 if (mSurfaceControl != null) { 536 mSurfaceControl.remove(); 537 mSurfaceControl = null; 538 } 539 if (mBackgroundControl != null) { 540 mBackgroundControl.remove(); 541 mBackgroundControl = null; 542 } 543 } 544 545 /** @hide */ updateSurface()546 protected void updateSurface() { 547 if (!mHaveFrame) { 548 return; 549 } 550 ViewRootImpl viewRoot = getViewRootImpl(); 551 if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) { 552 return; 553 } 554 555 mTranslator = viewRoot.mTranslator; 556 if (mTranslator != null) { 557 mSurface.setCompatibilityTranslator(mTranslator); 558 } 559 560 int myWidth = mRequestedWidth; 561 if (myWidth <= 0) myWidth = getWidth(); 562 int myHeight = mRequestedHeight; 563 if (myHeight <= 0) myHeight = getHeight(); 564 565 final boolean formatChanged = mFormat != mRequestedFormat; 566 final boolean visibleChanged = mVisible != mRequestedVisible; 567 final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged) 568 && mRequestedVisible; 569 final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight; 570 final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility; 571 boolean redrawNeeded = false; 572 573 if (creating || formatChanged || sizeChanged || visibleChanged || windowVisibleChanged) { 574 getLocationInWindow(mLocation); 575 576 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " 577 + "Changes: creating=" + creating 578 + " format=" + formatChanged + " size=" + sizeChanged 579 + " visible=" + visibleChanged 580 + " left=" + (mWindowSpaceLeft != mLocation[0]) 581 + " top=" + (mWindowSpaceTop != mLocation[1])); 582 583 try { 584 final boolean visible = mVisible = mRequestedVisible; 585 mWindowSpaceLeft = mLocation[0]; 586 mWindowSpaceTop = mLocation[1]; 587 mSurfaceWidth = myWidth; 588 mSurfaceHeight = myHeight; 589 mFormat = mRequestedFormat; 590 mLastWindowVisibility = mWindowVisibility; 591 592 mScreenRect.left = mWindowSpaceLeft; 593 mScreenRect.top = mWindowSpaceTop; 594 mScreenRect.right = mWindowSpaceLeft + getWidth(); 595 mScreenRect.bottom = mWindowSpaceTop + getHeight(); 596 if (mTranslator != null) { 597 mTranslator.translateRectInAppWindowToScreen(mScreenRect); 598 } 599 600 final Rect surfaceInsets = getParentSurfaceInsets(); 601 mScreenRect.offset(surfaceInsets.left, surfaceInsets.top); 602 603 if (creating) { 604 viewRoot.createBoundsSurface(mSubLayer); 605 mSurfaceSession = new SurfaceSession(); 606 mDeferredDestroySurfaceControl = mSurfaceControl; 607 608 updateOpaqueFlag(); 609 final String name = "SurfaceView - " + viewRoot.getTitle().toString(); 610 611 mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) 612 .setName(name) 613 .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0) 614 .setBufferSize(mSurfaceWidth, mSurfaceHeight) 615 .setFormat(mFormat) 616 .setParent(viewRoot.getSurfaceControl()) 617 .setFlags(mSurfaceFlags) 618 .build(); 619 mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession) 620 .setName("Background for -" + name) 621 .setOpaque(true) 622 .setColorLayer() 623 .setParent(mSurfaceControl) 624 .build(); 625 626 } else if (mSurfaceControl == null) { 627 return; 628 } 629 630 boolean realSizeChanged = false; 631 632 mSurfaceLock.lock(); 633 try { 634 mDrawingStopped = !visible; 635 636 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " 637 + "Cur surface: " + mSurface); 638 639 SurfaceControl.openTransaction(); 640 try { 641 mSurfaceControl.setLayer(mSubLayer); 642 643 if (mViewVisibility) { 644 mSurfaceControl.show(); 645 } else { 646 mSurfaceControl.hide(); 647 } 648 updateBackgroundVisibilityInTransaction(viewRoot.getSurfaceControl()); 649 650 // While creating the surface, we will set it's initial 651 // geometry. Outside of that though, we should generally 652 // leave it to the RenderThread. 653 // 654 // There is one more case when the buffer size changes we aren't yet 655 // prepared to sync (as even following the transaction applying 656 // we still need to latch a buffer). 657 // b/28866173 658 if (sizeChanged || creating || !mRtHandlingPositionUpdates) { 659 mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top); 660 mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth, 661 0.0f, 0.0f, 662 mScreenRect.height() / (float) mSurfaceHeight); 663 // Set a window crop when creating the surface or changing its size to 664 // crop the buffer to the surface size since the buffer producer may 665 // use SCALING_MODE_SCALE and submit a larger size than the surface 666 // size. 667 mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight); 668 } 669 mSurfaceControl.setCornerRadius(mCornerRadius); 670 if (sizeChanged && !creating) { 671 mSurfaceControl.setBufferSize(mSurfaceWidth, mSurfaceHeight); 672 } 673 } finally { 674 SurfaceControl.closeTransaction(); 675 } 676 677 if (sizeChanged || creating) { 678 redrawNeeded = true; 679 } 680 681 mSurfaceFrame.left = 0; 682 mSurfaceFrame.top = 0; 683 if (mTranslator == null) { 684 mSurfaceFrame.right = mSurfaceWidth; 685 mSurfaceFrame.bottom = mSurfaceHeight; 686 } else { 687 float appInvertedScale = mTranslator.applicationInvertedScale; 688 mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f); 689 mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f); 690 } 691 692 final int surfaceWidth = mSurfaceFrame.right; 693 final int surfaceHeight = mSurfaceFrame.bottom; 694 realSizeChanged = mLastSurfaceWidth != surfaceWidth 695 || mLastSurfaceHeight != surfaceHeight; 696 mLastSurfaceWidth = surfaceWidth; 697 mLastSurfaceHeight = surfaceHeight; 698 } finally { 699 mSurfaceLock.unlock(); 700 } 701 702 try { 703 redrawNeeded |= visible && !mDrawFinished; 704 705 SurfaceHolder.Callback callbacks[] = null; 706 707 final boolean surfaceChanged = creating; 708 if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) { 709 mSurfaceCreated = false; 710 if (mSurface.isValid()) { 711 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " 712 + "visibleChanged -- surfaceDestroyed"); 713 callbacks = getSurfaceCallbacks(); 714 for (SurfaceHolder.Callback c : callbacks) { 715 c.surfaceDestroyed(mSurfaceHolder); 716 } 717 // Since Android N the same surface may be reused and given to us 718 // again by the system server at a later point. However 719 // as we didn't do this in previous releases, clients weren't 720 // necessarily required to clean up properly in 721 // surfaceDestroyed. This leads to problems for example when 722 // clients don't destroy their EGL context, and try 723 // and create a new one on the same surface following reuse. 724 // Since there is no valid use of the surface in-between 725 // surfaceDestroyed and surfaceCreated, we force a disconnect, 726 // so the next connect will always work if we end up reusing 727 // the surface. 728 if (mSurface.isValid()) { 729 mSurface.forceScopedDisconnect(); 730 } 731 } 732 } 733 734 if (creating) { 735 mSurface.copyFrom(mSurfaceControl); 736 } 737 738 if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion 739 < Build.VERSION_CODES.O) { 740 // Some legacy applications use the underlying native {@link Surface} object 741 // as a key to whether anything has changed. In these cases, updates to the 742 // existing {@link Surface} will be ignored when the size changes. 743 // Therefore, we must explicitly recreate the {@link Surface} in these 744 // cases. 745 mSurface.createFrom(mSurfaceControl); 746 } 747 748 if (visible && mSurface.isValid()) { 749 if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) { 750 mSurfaceCreated = true; 751 mIsCreating = true; 752 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " 753 + "visibleChanged -- surfaceCreated"); 754 if (callbacks == null) { 755 callbacks = getSurfaceCallbacks(); 756 } 757 for (SurfaceHolder.Callback c : callbacks) { 758 c.surfaceCreated(mSurfaceHolder); 759 } 760 } 761 if (creating || formatChanged || sizeChanged 762 || visibleChanged || realSizeChanged) { 763 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " 764 + "surfaceChanged -- format=" + mFormat 765 + " w=" + myWidth + " h=" + myHeight); 766 if (callbacks == null) { 767 callbacks = getSurfaceCallbacks(); 768 } 769 for (SurfaceHolder.Callback c : callbacks) { 770 c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight); 771 } 772 } 773 if (redrawNeeded) { 774 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " 775 + "surfaceRedrawNeeded"); 776 if (callbacks == null) { 777 callbacks = getSurfaceCallbacks(); 778 } 779 780 mPendingReportDraws++; 781 viewRoot.drawPending(); 782 SurfaceCallbackHelper sch = 783 new SurfaceCallbackHelper(this::onDrawFinished); 784 sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); 785 } 786 } 787 } finally { 788 mIsCreating = false; 789 if (mSurfaceControl != null && !mSurfaceCreated) { 790 mSurface.release(); 791 792 releaseSurfaces(); 793 } 794 } 795 } catch (Exception ex) { 796 Log.e(TAG, "Exception configuring surface", ex); 797 } 798 if (DEBUG) Log.v( 799 TAG, "Layout: x=" + mScreenRect.left + " y=" + mScreenRect.top 800 + " w=" + mScreenRect.width() + " h=" + mScreenRect.height() 801 + ", frame=" + mSurfaceFrame); 802 } else { 803 // Calculate the window position in case RT loses the window 804 // and we need to fallback to a UI-thread driven position update 805 getLocationInSurface(mLocation); 806 final boolean positionChanged = mWindowSpaceLeft != mLocation[0] 807 || mWindowSpaceTop != mLocation[1]; 808 final boolean layoutSizeChanged = getWidth() != mScreenRect.width() 809 || getHeight() != mScreenRect.height(); 810 if (positionChanged || layoutSizeChanged) { // Only the position has changed 811 mWindowSpaceLeft = mLocation[0]; 812 mWindowSpaceTop = mLocation[1]; 813 // For our size changed check, we keep mScreenRect.width() and mScreenRect.height() 814 // in view local space. 815 mLocation[0] = getWidth(); 816 mLocation[1] = getHeight(); 817 818 mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop, 819 mWindowSpaceLeft + mLocation[0], mWindowSpaceTop + mLocation[1]); 820 821 if (mTranslator != null) { 822 mTranslator.translateRectInAppWindowToScreen(mScreenRect); 823 } 824 825 if (mSurfaceControl == null) { 826 return; 827 } 828 829 if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) { 830 try { 831 if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition UI, " + 832 "postion = [%d, %d, %d, %d]", System.identityHashCode(this), 833 mScreenRect.left, mScreenRect.top, 834 mScreenRect.right, mScreenRect.bottom)); 835 setParentSpaceRectangle(mScreenRect, -1); 836 } catch (Exception ex) { 837 Log.e(TAG, "Exception configuring surface", ex); 838 } 839 } 840 } 841 } 842 } 843 onDrawFinished()844 private void onDrawFinished() { 845 if (DEBUG) { 846 Log.i(TAG, System.identityHashCode(this) + " " 847 + "finishedDrawing"); 848 } 849 850 if (mDeferredDestroySurfaceControl != null) { 851 mDeferredDestroySurfaceControl.remove(); 852 mDeferredDestroySurfaceControl = null; 853 } 854 855 runOnUiThread(() -> { 856 performDrawFinished(); 857 }); 858 } 859 860 /** 861 * A place to over-ride for applying child-surface transactions. 862 * These can be synchronized with the viewroot surface using deferTransaction. 863 * 864 * Called from RenderWorker while UI thread is paused. 865 * @hide 866 */ applyChildSurfaceTransaction_renderWorker(SurfaceControl.Transaction t, Surface viewRootSurface, long nextViewRootFrameNumber)867 protected void applyChildSurfaceTransaction_renderWorker(SurfaceControl.Transaction t, 868 Surface viewRootSurface, long nextViewRootFrameNumber) { 869 } 870 applySurfaceTransforms(SurfaceControl surface, Rect position, long frameNumber)871 private void applySurfaceTransforms(SurfaceControl surface, Rect position, long frameNumber) { 872 if (frameNumber > 0) { 873 final ViewRootImpl viewRoot = getViewRootImpl(); 874 875 mRtTransaction.deferTransactionUntilSurface(surface, viewRoot.mSurface, 876 frameNumber); 877 } 878 879 mRtTransaction.setPosition(surface, position.left, position.top); 880 mRtTransaction.setMatrix(surface, 881 position.width() / (float) mSurfaceWidth, 882 0.0f, 0.0f, 883 position.height() / (float) mSurfaceHeight); 884 if (mViewVisibility) { 885 mRtTransaction.show(surface); 886 } 887 888 } 889 setParentSpaceRectangle(Rect position, long frameNumber)890 private void setParentSpaceRectangle(Rect position, long frameNumber) { 891 final ViewRootImpl viewRoot = getViewRootImpl(); 892 893 applySurfaceTransforms(mSurfaceControl, position, frameNumber); 894 895 applyChildSurfaceTransaction_renderWorker(mRtTransaction, viewRoot.mSurface, 896 frameNumber); 897 898 mRtTransaction.apply(); 899 } 900 901 private Rect mRTLastReportedPosition = new Rect(); 902 903 private RenderNode.PositionUpdateListener mPositionListener = 904 new RenderNode.PositionUpdateListener() { 905 906 @Override 907 public void positionChanged(long frameNumber, int left, int top, int right, int bottom) { 908 if (mSurfaceControl == null) { 909 return; 910 } 911 912 // TODO: This is teensy bit racey in that a brand new SurfaceView moving on 913 // its 2nd frame if RenderThread is running slowly could potentially see 914 // this as false, enter the branch, get pre-empted, then this comes along 915 // and reports a new position, then the UI thread resumes and reports 916 // its position. This could therefore be de-sync'd in that interval, but 917 // the synchronization would violate the rule that RT must never block 918 // on the UI thread which would open up potential deadlocks. The risk of 919 // a single-frame desync is therefore preferable for now. 920 mRtHandlingPositionUpdates = true; 921 if (mRTLastReportedPosition.left == left 922 && mRTLastReportedPosition.top == top 923 && mRTLastReportedPosition.right == right 924 && mRTLastReportedPosition.bottom == bottom) { 925 return; 926 } 927 try { 928 if (DEBUG) { 929 Log.d(TAG, String.format( 930 "%d updateSurfacePosition RenderWorker, frameNr = %d, " 931 + "postion = [%d, %d, %d, %d]", 932 System.identityHashCode(this), frameNumber, 933 left, top, right, bottom)); 934 } 935 mRTLastReportedPosition.set(left, top, right, bottom); 936 setParentSpaceRectangle(mRTLastReportedPosition, frameNumber); 937 // Now overwrite mRTLastReportedPosition with our values 938 } catch (Exception ex) { 939 Log.e(TAG, "Exception from repositionChild", ex); 940 } 941 } 942 943 @Override 944 public void positionLost(long frameNumber) { 945 if (DEBUG) { 946 Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d", 947 System.identityHashCode(this), frameNumber)); 948 } 949 mRTLastReportedPosition.setEmpty(); 950 951 if (mSurfaceControl == null) { 952 return; 953 } 954 955 if (frameNumber > 0) { 956 final ViewRootImpl viewRoot = getViewRootImpl(); 957 958 mRtTransaction.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface, 959 frameNumber); 960 } 961 mRtTransaction.hide(mSurfaceControl); 962 mRtTransaction.apply(); 963 } 964 }; 965 getSurfaceCallbacks()966 private SurfaceHolder.Callback[] getSurfaceCallbacks() { 967 SurfaceHolder.Callback callbacks[]; 968 synchronized (mCallbacks) { 969 callbacks = new SurfaceHolder.Callback[mCallbacks.size()]; 970 mCallbacks.toArray(callbacks); 971 } 972 return callbacks; 973 } 974 runOnUiThread(Runnable runnable)975 private void runOnUiThread(Runnable runnable) { 976 Handler handler = getHandler(); 977 if (handler != null && handler.getLooper() != Looper.myLooper()) { 978 handler.post(runnable); 979 } else { 980 runnable.run(); 981 } 982 } 983 984 /** 985 * Check to see if the surface has fixed size dimensions or if the surface's 986 * dimensions are dimensions are dependent on its current layout. 987 * 988 * @return true if the surface has dimensions that are fixed in size 989 * @hide 990 */ 991 @UnsupportedAppUsage isFixedSize()992 public boolean isFixedSize() { 993 return (mRequestedWidth != -1 || mRequestedHeight != -1); 994 } 995 isAboveParent()996 private boolean isAboveParent() { 997 return mSubLayer >= 0; 998 } 999 1000 /** 1001 * Set an opaque background color to use with this {@link SurfaceView} when it's being resized 1002 * and size of the content hasn't updated yet. This color will fill the expanded area when the 1003 * view becomes larger. 1004 * @param bgColor An opaque color to fill the background. Alpha component will be ignored. 1005 * @hide 1006 */ setResizeBackgroundColor(int bgColor)1007 public void setResizeBackgroundColor(int bgColor) { 1008 if (mBackgroundControl == null) { 1009 return; 1010 } 1011 1012 final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f, 1013 Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f }; 1014 1015 SurfaceControl.openTransaction(); 1016 try { 1017 mBackgroundControl.setColor(colorComponents); 1018 } finally { 1019 SurfaceControl.closeTransaction(); 1020 } 1021 } 1022 1023 @UnsupportedAppUsage 1024 private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() { 1025 private static final String LOG_TAG = "SurfaceHolder"; 1026 1027 @Override 1028 public boolean isCreating() { 1029 return mIsCreating; 1030 } 1031 1032 @Override 1033 public void addCallback(Callback callback) { 1034 synchronized (mCallbacks) { 1035 // This is a linear search, but in practice we'll 1036 // have only a couple callbacks, so it doesn't matter. 1037 if (mCallbacks.contains(callback) == false) { 1038 mCallbacks.add(callback); 1039 } 1040 } 1041 } 1042 1043 @Override 1044 public void removeCallback(Callback callback) { 1045 synchronized (mCallbacks) { 1046 mCallbacks.remove(callback); 1047 } 1048 } 1049 1050 @Override 1051 public void setFixedSize(int width, int height) { 1052 if (mRequestedWidth != width || mRequestedHeight != height) { 1053 mRequestedWidth = width; 1054 mRequestedHeight = height; 1055 requestLayout(); 1056 } 1057 } 1058 1059 @Override 1060 public void setSizeFromLayout() { 1061 if (mRequestedWidth != -1 || mRequestedHeight != -1) { 1062 mRequestedWidth = mRequestedHeight = -1; 1063 requestLayout(); 1064 } 1065 } 1066 1067 @Override 1068 public void setFormat(int format) { 1069 // for backward compatibility reason, OPAQUE always 1070 // means 565 for SurfaceView 1071 if (format == PixelFormat.OPAQUE) 1072 format = PixelFormat.RGB_565; 1073 1074 mRequestedFormat = format; 1075 if (mSurfaceControl != null) { 1076 updateSurface(); 1077 } 1078 } 1079 1080 /** 1081 * @deprecated setType is now ignored. 1082 */ 1083 @Override 1084 @Deprecated 1085 public void setType(int type) { } 1086 1087 @Override 1088 public void setKeepScreenOn(boolean screenOn) { 1089 runOnUiThread(() -> SurfaceView.this.setKeepScreenOn(screenOn)); 1090 } 1091 1092 /** 1093 * Gets a {@link Canvas} for drawing into the SurfaceView's Surface 1094 * 1095 * After drawing into the provided {@link Canvas}, the caller must 1096 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. 1097 * 1098 * The caller must redraw the entire surface. 1099 * @return A canvas for drawing into the surface. 1100 */ 1101 @Override 1102 public Canvas lockCanvas() { 1103 return internalLockCanvas(null, false); 1104 } 1105 1106 /** 1107 * Gets a {@link Canvas} for drawing into the SurfaceView's Surface 1108 * 1109 * After drawing into the provided {@link Canvas}, the caller must 1110 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. 1111 * 1112 * @param inOutDirty A rectangle that represents the dirty region that the caller wants 1113 * to redraw. This function may choose to expand the dirty rectangle if for example 1114 * the surface has been resized or if the previous contents of the surface were 1115 * not available. The caller must redraw the entire dirty region as represented 1116 * by the contents of the inOutDirty rectangle upon return from this function. 1117 * The caller may also pass <code>null</code> instead, in the case where the 1118 * entire surface should be redrawn. 1119 * @return A canvas for drawing into the surface. 1120 */ 1121 @Override 1122 public Canvas lockCanvas(Rect inOutDirty) { 1123 return internalLockCanvas(inOutDirty, false); 1124 } 1125 1126 @Override 1127 public Canvas lockHardwareCanvas() { 1128 return internalLockCanvas(null, true); 1129 } 1130 1131 private Canvas internalLockCanvas(Rect dirty, boolean hardware) { 1132 mSurfaceLock.lock(); 1133 1134 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped=" 1135 + mDrawingStopped + ", surfaceControl=" + mSurfaceControl); 1136 1137 Canvas c = null; 1138 if (!mDrawingStopped && mSurfaceControl != null) { 1139 try { 1140 if (hardware) { 1141 c = mSurface.lockHardwareCanvas(); 1142 } else { 1143 c = mSurface.lockCanvas(dirty); 1144 } 1145 } catch (Exception e) { 1146 Log.e(LOG_TAG, "Exception locking surface", e); 1147 } 1148 } 1149 1150 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Returned canvas: " + c); 1151 if (c != null) { 1152 mLastLockTime = SystemClock.uptimeMillis(); 1153 return c; 1154 } 1155 1156 // If the Surface is not ready to be drawn, then return null, 1157 // but throttle calls to this function so it isn't called more 1158 // than every 100ms. 1159 long now = SystemClock.uptimeMillis(); 1160 long nextTime = mLastLockTime + 100; 1161 if (nextTime > now) { 1162 try { 1163 Thread.sleep(nextTime-now); 1164 } catch (InterruptedException e) { 1165 } 1166 now = SystemClock.uptimeMillis(); 1167 } 1168 mLastLockTime = now; 1169 mSurfaceLock.unlock(); 1170 1171 return null; 1172 } 1173 1174 /** 1175 * Posts the new contents of the {@link Canvas} to the surface and 1176 * releases the {@link Canvas}. 1177 * 1178 * @param canvas The canvas previously obtained from {@link #lockCanvas}. 1179 */ 1180 @Override 1181 public void unlockCanvasAndPost(Canvas canvas) { 1182 mSurface.unlockCanvasAndPost(canvas); 1183 mSurfaceLock.unlock(); 1184 } 1185 1186 @Override 1187 public Surface getSurface() { 1188 return mSurface; 1189 } 1190 1191 @Override 1192 public Rect getSurfaceFrame() { 1193 return mSurfaceFrame; 1194 } 1195 }; 1196 1197 /** 1198 * Return a SurfaceControl which can be used for parenting Surfaces to 1199 * this SurfaceView. 1200 * 1201 * @return The SurfaceControl for this SurfaceView. 1202 */ getSurfaceControl()1203 public SurfaceControl getSurfaceControl() { 1204 return mSurfaceControl; 1205 } 1206 } 1207