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