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