1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view; 18 19 import com.android.internal.view.BaseIWindow; 20 21 import android.content.Context; 22 import android.content.res.Configuration; 23 import android.content.res.CompatibilityInfo.Translator; 24 import android.graphics.Canvas; 25 import android.graphics.PixelFormat; 26 import android.graphics.PorterDuff; 27 import android.graphics.Rect; 28 import android.graphics.Region; 29 import android.os.Handler; 30 import android.os.Message; 31 import android.os.RemoteException; 32 import android.os.SystemClock; 33 import android.os.ParcelFileDescriptor; 34 import android.util.AttributeSet; 35 import android.util.Log; 36 37 import java.lang.ref.WeakReference; 38 import java.util.ArrayList; 39 import java.util.concurrent.locks.ReentrantLock; 40 41 /** 42 * Provides a dedicated drawing surface embedded inside of a view hierarchy. 43 * You can control the format of this surface and, if you like, its size; the 44 * SurfaceView takes care of placing the surface at the correct location on the 45 * screen 46 * 47 * <p>The surface is Z ordered so that it is behind the window holding its 48 * SurfaceView; the SurfaceView punches a hole in its window to allow its 49 * surface to be displayed. The view hierarchy will take care of correctly 50 * compositing with the Surface any siblings of the SurfaceView that would 51 * normally appear on top of it. This can be used to place overlays such as 52 * buttons on top of the Surface, though note however that it can have an 53 * impact on performance since a full alpha-blended composite will be performed 54 * each time the Surface changes. 55 * 56 * <p> The transparent region that makes the surface visible is based on the 57 * layout positions in the view hierarchy. If the post-layout transform 58 * properties are used to draw a sibling view on top of the SurfaceView, the 59 * view may not be properly composited with the surface. 60 * 61 * <p>Access to the underlying surface is provided via the SurfaceHolder interface, 62 * which can be retrieved by calling {@link #getHolder}. 63 * 64 * <p>The Surface will be created for you while the SurfaceView's window is 65 * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated} 66 * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the 67 * Surface is created and destroyed as the window is shown and hidden. 68 * 69 * <p>One of the purposes of this class is to provide a surface in which a 70 * secondary thread can render into the screen. If you are going to use it 71 * this way, you need to be aware of some threading semantics: 72 * 73 * <ul> 74 * <li> All SurfaceView and 75 * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called 76 * from the thread running the SurfaceView's window (typically the main thread 77 * of the application). They thus need to correctly synchronize with any 78 * state that is also touched by the drawing thread. 79 * <li> You must ensure that the drawing thread only touches the underlying 80 * Surface while it is valid -- between 81 * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()} 82 * and 83 * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}. 84 * </ul> 85 * 86 * <p class="note"><strong>Note:</strong> Starting in platform version 87 * {@link android.os.Build.VERSION_CODES#N}, SurfaceView's window position is 88 * updated synchronously with other View rendering. This means that translating 89 * and scaling a SurfaceView on screen will not cause rendering artifacts. Such 90 * artifacts may occur on previous versions of the platform when its window is 91 * positioned asynchronously.</p> 92 */ 93 public class SurfaceView extends View { 94 static private final String TAG = "SurfaceView"; 95 static private final boolean DEBUG = false; 96 97 final ArrayList<SurfaceHolder.Callback> mCallbacks 98 = new ArrayList<SurfaceHolder.Callback>(); 99 100 final int[] mLocation = new int[2]; 101 102 final ReentrantLock mSurfaceLock = new ReentrantLock(); 103 final Surface mSurface = new Surface(); // Current surface in use 104 final Surface mNewSurface = new Surface(); // New surface we are switching to 105 boolean mDrawingStopped = true; 106 107 final WindowManager.LayoutParams mLayout 108 = new WindowManager.LayoutParams(); 109 IWindowSession mSession; 110 MyWindow mWindow; 111 final Rect mVisibleInsets = new Rect(); 112 final Rect mWinFrame = new Rect(); 113 final Rect mOverscanInsets = new Rect(); 114 final Rect mContentInsets = new Rect(); 115 final Rect mStableInsets = new Rect(); 116 final Rect mOutsets = new Rect(); 117 final Rect mBackdropFrame = new Rect(); 118 final Configuration mConfiguration = new Configuration(); 119 120 static final int KEEP_SCREEN_ON_MSG = 1; 121 static final int GET_NEW_SURFACE_MSG = 2; 122 static final int UPDATE_WINDOW_MSG = 3; 123 124 int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; 125 126 boolean mIsCreating = false; 127 private volatile boolean mRtHandlingPositionUpdates = false; 128 129 final Handler mHandler = new Handler() { 130 @Override 131 public void handleMessage(Message msg) { 132 switch (msg.what) { 133 case KEEP_SCREEN_ON_MSG: { 134 setKeepScreenOn(msg.arg1 != 0); 135 } break; 136 case GET_NEW_SURFACE_MSG: { 137 handleGetNewSurface(); 138 } break; 139 case UPDATE_WINDOW_MSG: { 140 updateWindow(false, false); 141 } break; 142 } 143 } 144 }; 145 146 private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener 147 = new ViewTreeObserver.OnScrollChangedListener() { 148 @Override 149 public void onScrollChanged() { 150 updateWindow(false, false); 151 } 152 }; 153 154 private final ViewTreeObserver.OnPreDrawListener mDrawListener = 155 new ViewTreeObserver.OnPreDrawListener() { 156 @Override 157 public boolean onPreDraw() { 158 // reposition ourselves where the surface is 159 mHaveFrame = getWidth() > 0 && getHeight() > 0; 160 updateWindow(false, false); 161 return true; 162 } 163 }; 164 165 boolean mRequestedVisible = false; 166 boolean mWindowVisibility = false; 167 boolean mViewVisibility = false; 168 int mRequestedWidth = -1; 169 int mRequestedHeight = -1; 170 /* Set SurfaceView's format to 565 by default to maintain backward 171 * compatibility with applications assuming this format. 172 */ 173 int mRequestedFormat = PixelFormat.RGB_565; 174 175 boolean mHaveFrame = false; 176 boolean mSurfaceCreated = false; 177 long mLastLockTime = 0; 178 179 boolean mVisible = false; 180 int mWindowSpaceLeft = -1; 181 int mWindowSpaceTop = -1; 182 int mWindowSpaceWidth = -1; 183 int mWindowSpaceHeight = -1; 184 int mFormat = -1; 185 final Rect mSurfaceFrame = new Rect(); 186 int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1; 187 boolean mUpdateWindowNeeded; 188 boolean mReportDrawNeeded; 189 private Translator mTranslator; 190 private int mWindowInsetLeft; 191 private int mWindowInsetTop; 192 193 private boolean mGlobalListenersAdded; 194 195 public SurfaceView(Context context) { 196 super(context); 197 init(); 198 } 199 200 public SurfaceView(Context context, AttributeSet attrs) { 201 super(context, attrs); 202 init(); 203 } 204 205 public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { 206 super(context, attrs, defStyleAttr); 207 init(); 208 } 209 210 public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 211 super(context, attrs, defStyleAttr, defStyleRes); 212 init(); 213 } 214 215 private void init() { 216 setWillNotDraw(true); 217 } 218 219 /** 220 * Return the SurfaceHolder providing access and control over this 221 * SurfaceView's underlying surface. 222 * 223 * @return SurfaceHolder The holder of the surface. 224 */ 225 public SurfaceHolder getHolder() { 226 return mSurfaceHolder; 227 } 228 229 @Override 230 protected void onAttachedToWindow() { 231 super.onAttachedToWindow(); 232 mParent.requestTransparentRegion(this); 233 mSession = getWindowSession(); 234 mLayout.token = getWindowToken(); 235 mLayout.setTitle("SurfaceView - " + getViewRootImpl().getTitle()); 236 mViewVisibility = getVisibility() == VISIBLE; 237 238 if (!mGlobalListenersAdded) { 239 ViewTreeObserver observer = getViewTreeObserver(); 240 observer.addOnScrollChangedListener(mScrollChangedListener); 241 observer.addOnPreDrawListener(mDrawListener); 242 mGlobalListenersAdded = true; 243 } 244 } 245 246 @Override 247 protected void onWindowVisibilityChanged(int visibility) { 248 super.onWindowVisibilityChanged(visibility); 249 mWindowVisibility = visibility == VISIBLE; 250 mRequestedVisible = mWindowVisibility && mViewVisibility; 251 updateWindow(false, false); 252 } 253 254 @Override 255 public void setVisibility(int visibility) { 256 super.setVisibility(visibility); 257 mViewVisibility = visibility == VISIBLE; 258 boolean newRequestedVisible = mWindowVisibility && mViewVisibility; 259 if (newRequestedVisible != mRequestedVisible) { 260 // our base class (View) invalidates the layout only when 261 // we go from/to the GONE state. However, SurfaceView needs 262 // to request a re-layout when the visibility changes at all. 263 // This is needed because the transparent region is computed 264 // as part of the layout phase, and it changes (obviously) when 265 // the visibility changes. 266 requestLayout(); 267 } 268 mRequestedVisible = newRequestedVisible; 269 updateWindow(false, false); 270 } 271 272 @Override 273 protected void onDetachedFromWindow() { 274 if (mGlobalListenersAdded) { 275 ViewTreeObserver observer = getViewTreeObserver(); 276 observer.removeOnScrollChangedListener(mScrollChangedListener); 277 observer.removeOnPreDrawListener(mDrawListener); 278 mGlobalListenersAdded = false; 279 } 280 281 mRequestedVisible = false; 282 updateWindow(false, false); 283 mHaveFrame = false; 284 if (mWindow != null) { 285 try { 286 mSession.remove(mWindow); 287 } catch (RemoteException ex) { 288 // Not much we can do here... 289 } 290 mWindow = null; 291 } 292 mSession = null; 293 mLayout.token = null; 294 295 super.onDetachedFromWindow(); 296 } 297 298 @Override 299 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 300 int width = mRequestedWidth >= 0 301 ? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0) 302 : getDefaultSize(0, widthMeasureSpec); 303 int height = mRequestedHeight >= 0 304 ? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0) 305 : getDefaultSize(0, heightMeasureSpec); 306 setMeasuredDimension(width, height); 307 } 308 309 /** @hide */ 310 @Override 311 protected boolean setFrame(int left, int top, int right, int bottom) { 312 boolean result = super.setFrame(left, top, right, bottom); 313 updateWindow(false, false); 314 return result; 315 } 316 317 @Override 318 public boolean gatherTransparentRegion(Region region) { 319 if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { 320 return super.gatherTransparentRegion(region); 321 } 322 323 boolean opaque = true; 324 if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) { 325 // this view draws, remove it from the transparent region 326 opaque = super.gatherTransparentRegion(region); 327 } else if (region != null) { 328 int w = getWidth(); 329 int h = getHeight(); 330 if (w>0 && h>0) { 331 getLocationInWindow(mLocation); 332 // otherwise, punch a hole in the whole hierarchy 333 int l = mLocation[0]; 334 int t = mLocation[1]; 335 region.op(l, t, l+w, t+h, Region.Op.UNION); 336 } 337 } 338 if (PixelFormat.formatHasAlpha(mRequestedFormat)) { 339 opaque = false; 340 } 341 return opaque; 342 } 343 344 @Override 345 public void draw(Canvas canvas) { 346 if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { 347 // draw() is not called when SKIP_DRAW is set 348 if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) { 349 // punch a whole in the view-hierarchy below us 350 canvas.drawColor(0, PorterDuff.Mode.CLEAR); 351 } 352 } 353 super.draw(canvas); 354 } 355 356 @Override 357 protected void dispatchDraw(Canvas canvas) { 358 if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { 359 // if SKIP_DRAW is cleared, draw() has already punched a hole 360 if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { 361 // punch a whole in the view-hierarchy below us 362 canvas.drawColor(0, PorterDuff.Mode.CLEAR); 363 } 364 } 365 super.dispatchDraw(canvas); 366 } 367 368 /** 369 * Control whether the surface view's surface is placed on top of another 370 * regular surface view in the window (but still behind the window itself). 371 * This is typically used to place overlays on top of an underlying media 372 * surface view. 373 * 374 * <p>Note that this must be set before the surface view's containing 375 * window is attached to the window manager. 376 * 377 * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}. 378 */ 379 public void setZOrderMediaOverlay(boolean isMediaOverlay) { 380 mWindowType = isMediaOverlay 381 ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY 382 : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; 383 } 384 385 /** 386 * Control whether the surface view's surface is placed on top of its 387 * window. Normally it is placed behind the window, to allow it to 388 * (for the most part) appear to composite with the views in the 389 * hierarchy. By setting this, you cause it to be placed above the 390 * window. This means that none of the contents of the window this 391 * SurfaceView is in will be visible on top of its surface. 392 * 393 * <p>Note that this must be set before the surface view's containing 394 * window is attached to the window manager. 395 * 396 * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}. 397 */ 398 public void setZOrderOnTop(boolean onTop) { 399 if (onTop) { 400 mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; 401 // ensures the surface is placed below the IME 402 mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 403 } else { 404 mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; 405 mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 406 } 407 } 408 409 /** 410 * Control whether the surface view's content should be treated as secure, 411 * preventing it from appearing in screenshots or from being viewed on 412 * non-secure displays. 413 * 414 * <p>Note that this must be set before the surface view's containing 415 * window is attached to the window manager. 416 * 417 * <p>See {@link android.view.Display#FLAG_SECURE} for details. 418 * 419 * @param isSecure True if the surface view is secure. 420 */ 421 public void setSecure(boolean isSecure) { 422 if (isSecure) { 423 mLayout.flags |= WindowManager.LayoutParams.FLAG_SECURE; 424 } else { 425 mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SECURE; 426 } 427 } 428 429 /** 430 * Hack to allow special layering of windows. The type is one of the 431 * types in WindowManager.LayoutParams. This is a hack so: 432 * @hide 433 */ 434 public void setWindowType(int type) { 435 mWindowType = type; 436 } 437 438 /** @hide */ 439 protected void updateWindow(boolean force, boolean redrawNeeded) { 440 if (!mHaveFrame) { 441 return; 442 } 443 ViewRootImpl viewRoot = getViewRootImpl(); 444 if (viewRoot != null) { 445 mTranslator = viewRoot.mTranslator; 446 } 447 448 if (mTranslator != null) { 449 mSurface.setCompatibilityTranslator(mTranslator); 450 } 451 452 int myWidth = mRequestedWidth; 453 if (myWidth <= 0) myWidth = getWidth(); 454 int myHeight = mRequestedHeight; 455 if (myHeight <= 0) myHeight = getHeight(); 456 457 final boolean creating = mWindow == null; 458 final boolean formatChanged = mFormat != mRequestedFormat; 459 final boolean sizeChanged = mWindowSpaceWidth != myWidth || mWindowSpaceHeight != myHeight; 460 final boolean visibleChanged = mVisible != mRequestedVisible; 461 final boolean layoutSizeChanged = getWidth() != mLayout.width 462 || getHeight() != mLayout.height; 463 464 if (force || creating || formatChanged || sizeChanged || visibleChanged 465 || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) { 466 getLocationInWindow(mLocation); 467 468 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " 469 + "Changes: creating=" + creating 470 + " format=" + formatChanged + " size=" + sizeChanged 471 + " visible=" + visibleChanged 472 + " left=" + (mWindowSpaceLeft != mLocation[0]) 473 + " top=" + (mWindowSpaceTop != mLocation[1])); 474 475 try { 476 final boolean visible = mVisible = mRequestedVisible; 477 mWindowSpaceLeft = mLocation[0]; 478 mWindowSpaceTop = mLocation[1]; 479 mWindowSpaceWidth = myWidth; 480 mWindowSpaceHeight = myHeight; 481 mFormat = mRequestedFormat; 482 483 // Scaling/Translate window's layout here because mLayout is not used elsewhere. 484 485 // Places the window relative 486 mLayout.x = mWindowSpaceLeft; 487 mLayout.y = mWindowSpaceTop; 488 mLayout.width = getWidth(); 489 mLayout.height = getHeight(); 490 if (mTranslator != null) { 491 mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout); 492 } 493 494 mLayout.format = mRequestedFormat; 495 mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 496 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 497 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 498 | WindowManager.LayoutParams.FLAG_SCALED 499 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 500 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 501 ; 502 if (!creating && !force && !mUpdateWindowNeeded && !sizeChanged) { 503 mLayout.privateFlags |= 504 WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY; 505 } else { 506 mLayout.privateFlags &= 507 ~WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY; 508 } 509 510 if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) { 511 mLayout.privateFlags |= 512 WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; 513 } 514 mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION 515 | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; 516 517 if (mWindow == null) { 518 Display display = getDisplay(); 519 mWindow = new MyWindow(this); 520 mLayout.type = mWindowType; 521 mLayout.gravity = Gravity.START|Gravity.TOP; 522 mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout, 523 mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets, 524 mStableInsets); 525 } 526 527 boolean realSizeChanged; 528 boolean reportDrawNeeded; 529 530 int relayoutResult; 531 532 mSurfaceLock.lock(); 533 try { 534 mUpdateWindowNeeded = false; 535 reportDrawNeeded = mReportDrawNeeded; 536 mReportDrawNeeded = false; 537 mDrawingStopped = !visible; 538 539 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " 540 + "Cur surface: " + mSurface); 541 542 relayoutResult = mSession.relayout( 543 mWindow, mWindow.mSeq, mLayout, mWindowSpaceWidth, mWindowSpaceHeight, 544 visible ? VISIBLE : GONE, 545 WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY, 546 mWinFrame, mOverscanInsets, mContentInsets, 547 mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame, 548 mConfiguration, mNewSurface); 549 if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { 550 reportDrawNeeded = true; 551 } 552 553 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " 554 + "New surface: " + mNewSurface 555 + ", vis=" + visible + ", frame=" + mWinFrame); 556 557 mSurfaceFrame.left = 0; 558 mSurfaceFrame.top = 0; 559 if (mTranslator == null) { 560 mSurfaceFrame.right = mWinFrame.width(); 561 mSurfaceFrame.bottom = mWinFrame.height(); 562 } else { 563 float appInvertedScale = mTranslator.applicationInvertedScale; 564 mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f); 565 mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f); 566 } 567 568 final int surfaceWidth = mSurfaceFrame.right; 569 final int surfaceHeight = mSurfaceFrame.bottom; 570 realSizeChanged = mLastSurfaceWidth != surfaceWidth 571 || mLastSurfaceHeight != surfaceHeight; 572 mLastSurfaceWidth = surfaceWidth; 573 mLastSurfaceHeight = surfaceHeight; 574 } finally { 575 mSurfaceLock.unlock(); 576 } 577 578 try { 579 redrawNeeded |= creating | reportDrawNeeded; 580 581 SurfaceHolder.Callback callbacks[] = null; 582 583 final boolean surfaceChanged = (relayoutResult 584 & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0; 585 if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) { 586 mSurfaceCreated = false; 587 if (mSurface.isValid()) { 588 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " 589 + "visibleChanged -- surfaceDestroyed"); 590 callbacks = getSurfaceCallbacks(); 591 for (SurfaceHolder.Callback c : callbacks) { 592 c.surfaceDestroyed(mSurfaceHolder); 593 } 594 } 595 } 596 597 mSurface.transferFrom(mNewSurface); 598 if (visible && mSurface.isValid()) { 599 if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) { 600 mSurfaceCreated = true; 601 mIsCreating = true; 602 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " 603 + "visibleChanged -- surfaceCreated"); 604 if (callbacks == null) { 605 callbacks = getSurfaceCallbacks(); 606 } 607 for (SurfaceHolder.Callback c : callbacks) { 608 c.surfaceCreated(mSurfaceHolder); 609 } 610 } 611 if (creating || formatChanged || sizeChanged 612 || visibleChanged || realSizeChanged) { 613 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " 614 + "surfaceChanged -- format=" + mFormat 615 + " w=" + myWidth + " h=" + myHeight); 616 if (callbacks == null) { 617 callbacks = getSurfaceCallbacks(); 618 } 619 for (SurfaceHolder.Callback c : callbacks) { 620 c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight); 621 } 622 } 623 if (redrawNeeded) { 624 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " 625 + "surfaceRedrawNeeded"); 626 if (callbacks == null) { 627 callbacks = getSurfaceCallbacks(); 628 } 629 for (SurfaceHolder.Callback c : callbacks) { 630 if (c instanceof SurfaceHolder.Callback2) { 631 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( 632 mSurfaceHolder); 633 } 634 } 635 } 636 } 637 } finally { 638 mIsCreating = false; 639 if (redrawNeeded) { 640 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " 641 + "finishedDrawing"); 642 mSession.finishDrawing(mWindow); 643 } 644 mSession.performDeferredDestroy(mWindow); 645 } 646 } catch (RemoteException ex) { 647 Log.e(TAG, "Exception from relayout", ex); 648 } 649 if (DEBUG) Log.v( 650 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 651 " w=" + mLayout.width + " h=" + mLayout.height + 652 ", frame=" + mSurfaceFrame); 653 } else { 654 // Calculate the window position in case RT loses the window 655 // and we need to fallback to a UI-thread driven position update 656 getLocationInWindow(mLocation); 657 final boolean positionChanged = mWindowSpaceLeft != mLocation[0] 658 || mWindowSpaceTop != mLocation[1]; 659 if (positionChanged || layoutSizeChanged) { // Only the position has changed 660 mWindowSpaceLeft = mLocation[0]; 661 mWindowSpaceTop = mLocation[1]; 662 // For our size changed check, we keep mLayout.width and mLayout.height 663 // in view local space. 664 mLocation[0] = mLayout.width = getWidth(); 665 mLocation[1] = mLayout.height = getHeight(); 666 667 transformFromViewToWindowSpace(mLocation); 668 669 mWinFrame.set(mWindowSpaceLeft, mWindowSpaceTop, 670 mLocation[0], mLocation[1]); 671 672 if (mTranslator != null) { 673 mTranslator.translateRectInAppWindowToScreen(mWinFrame); 674 } 675 676 if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) { 677 try { 678 if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition UI, " + 679 "postion = [%d, %d, %d, %d]", System.identityHashCode(this), 680 mWinFrame.left, mWinFrame.top, 681 mWinFrame.right, mWinFrame.bottom)); 682 mSession.repositionChild(mWindow, mWinFrame.left, mWinFrame.top, 683 mWinFrame.right, mWinFrame.bottom, -1, mWinFrame); 684 } catch (RemoteException ex) { 685 Log.e(TAG, "Exception from relayout", ex); 686 } 687 } 688 } 689 } 690 } 691 692 private Rect mRTLastReportedPosition = new Rect(); 693 694 /** 695 * Called by native on RenderThread to update the window position 696 * @hide 697 */ 698 public final void updateWindowPositionRT(long frameNumber, 699 int left, int top, int right, int bottom) { 700 IWindowSession session = mSession; 701 MyWindow window = mWindow; 702 if (session == null || window == null) { 703 // Guess we got detached, that sucks 704 return; 705 } 706 // TODO: This is teensy bit racey in that a brand new SurfaceView moving on 707 // its 2nd frame if RenderThread is running slowly could potentially see 708 // this as false, enter the branch, get pre-empted, then this comes along 709 // and reports a new position, then the UI thread resumes and reports 710 // its position. This could therefore be de-sync'd in that interval, but 711 // the synchronization would violate the rule that RT must never block 712 // on the UI thread which would open up potential deadlocks. The risk of 713 // a single-frame desync is therefore preferable for now. 714 mRtHandlingPositionUpdates = true; 715 if (mRTLastReportedPosition.left == left 716 && mRTLastReportedPosition.top == top 717 && mRTLastReportedPosition.right == right 718 && mRTLastReportedPosition.bottom == bottom) { 719 return; 720 } 721 try { 722 if (DEBUG) { 723 Log.d(TAG, String.format("%d updateWindowPosition RT, frameNr = %d, " + 724 "postion = [%d, %d, %d, %d]", System.identityHashCode(this), 725 frameNumber, left, top, right, bottom)); 726 } 727 // Just using mRTLastReportedPosition as a dummy rect here 728 session.repositionChild(window, left, top, right, bottom, 729 frameNumber, 730 mRTLastReportedPosition); 731 // Now overwrite mRTLastReportedPosition with our values 732 mRTLastReportedPosition.set(left, top, right, bottom); 733 } catch (RemoteException ex) { 734 Log.e(TAG, "Exception from repositionChild", ex); 735 } 736 } 737 738 /** 739 * Called by native on RenderThread to notify that the window is no longer in the 740 * draw tree 741 * @hide 742 */ 743 public final void windowPositionLostRT(long frameNumber) { 744 if (DEBUG) { 745 Log.d(TAG, String.format("%d windowPositionLostRT RT, frameNr = %d", 746 System.identityHashCode(this), frameNumber)); 747 } 748 IWindowSession session = mSession; 749 MyWindow window = mWindow; 750 if (session == null || window == null) { 751 // We got detached prior to receiving this, abort 752 return; 753 } 754 if (mRtHandlingPositionUpdates) { 755 mRtHandlingPositionUpdates = false; 756 // This callback will happen while the UI thread is blocked, so we can 757 // safely access other member variables at this time. 758 // So do what the UI thread would have done if RT wasn't handling position 759 // updates. 760 if (!mWinFrame.isEmpty() && !mWinFrame.equals(mRTLastReportedPosition)) { 761 try { 762 if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition, " + 763 "postion = [%d, %d, %d, %d]", System.identityHashCode(this), 764 mWinFrame.left, mWinFrame.top, 765 mWinFrame.right, mWinFrame.bottom)); 766 session.repositionChild(window, mWinFrame.left, mWinFrame.top, 767 mWinFrame.right, mWinFrame.bottom, frameNumber, mWinFrame); 768 } catch (RemoteException ex) { 769 Log.e(TAG, "Exception from relayout", ex); 770 } 771 } 772 mRTLastReportedPosition.setEmpty(); 773 } 774 } 775 776 private SurfaceHolder.Callback[] getSurfaceCallbacks() { 777 SurfaceHolder.Callback callbacks[]; 778 synchronized (mCallbacks) { 779 callbacks = new SurfaceHolder.Callback[mCallbacks.size()]; 780 mCallbacks.toArray(callbacks); 781 } 782 return callbacks; 783 } 784 785 void handleGetNewSurface() { 786 updateWindow(false, false); 787 } 788 789 /** 790 * Check to see if the surface has fixed size dimensions or if the surface's 791 * dimensions are dimensions are dependent on its current layout. 792 * 793 * @return true if the surface has dimensions that are fixed in size 794 * @hide 795 */ 796 public boolean isFixedSize() { 797 return (mRequestedWidth != -1 || mRequestedHeight != -1); 798 } 799 800 private static class MyWindow extends BaseIWindow { 801 private final WeakReference<SurfaceView> mSurfaceView; 802 803 public MyWindow(SurfaceView surfaceView) { 804 mSurfaceView = new WeakReference<SurfaceView>(surfaceView); 805 } 806 807 @Override 808 public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, 809 Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, 810 Configuration newConfig, Rect backDropRect, boolean forceLayout, 811 boolean alwaysConsumeNavBar) { 812 SurfaceView surfaceView = mSurfaceView.get(); 813 if (surfaceView != null) { 814 if (DEBUG) Log.v(TAG, surfaceView + " got resized: w=" + frame.width() 815 + " h=" + frame.height() + ", cur w=" + mCurWidth + " h=" + mCurHeight); 816 surfaceView.mSurfaceLock.lock(); 817 try { 818 if (reportDraw) { 819 surfaceView.mUpdateWindowNeeded = true; 820 surfaceView.mReportDrawNeeded = true; 821 surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG); 822 } else if (surfaceView.mWinFrame.width() != frame.width() 823 || surfaceView.mWinFrame.height() != frame.height() 824 || forceLayout) { 825 surfaceView.mUpdateWindowNeeded = true; 826 surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG); 827 } 828 } finally { 829 surfaceView.mSurfaceLock.unlock(); 830 } 831 } 832 } 833 834 @Override 835 public void dispatchAppVisibility(boolean visible) { 836 // The point of SurfaceView is to let the app control the surface. 837 } 838 839 @Override 840 public void dispatchGetNewSurface() { 841 SurfaceView surfaceView = mSurfaceView.get(); 842 if (surfaceView != null) { 843 Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG); 844 surfaceView.mHandler.sendMessage(msg); 845 } 846 } 847 848 @Override 849 public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) { 850 Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled); 851 } 852 853 @Override 854 public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { 855 } 856 857 int mCurWidth = -1; 858 int mCurHeight = -1; 859 } 860 861 private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() { 862 863 private static final String LOG_TAG = "SurfaceHolder"; 864 865 @Override 866 public boolean isCreating() { 867 return mIsCreating; 868 } 869 870 @Override 871 public void addCallback(Callback callback) { 872 synchronized (mCallbacks) { 873 // This is a linear search, but in practice we'll 874 // have only a couple callbacks, so it doesn't matter. 875 if (mCallbacks.contains(callback) == false) { 876 mCallbacks.add(callback); 877 } 878 } 879 } 880 881 @Override 882 public void removeCallback(Callback callback) { 883 synchronized (mCallbacks) { 884 mCallbacks.remove(callback); 885 } 886 } 887 888 @Override 889 public void setFixedSize(int width, int height) { 890 if (mRequestedWidth != width || mRequestedHeight != height) { 891 mRequestedWidth = width; 892 mRequestedHeight = height; 893 requestLayout(); 894 } 895 } 896 897 @Override 898 public void setSizeFromLayout() { 899 if (mRequestedWidth != -1 || mRequestedHeight != -1) { 900 mRequestedWidth = mRequestedHeight = -1; 901 requestLayout(); 902 } 903 } 904 905 @Override 906 public void setFormat(int format) { 907 908 // for backward compatibility reason, OPAQUE always 909 // means 565 for SurfaceView 910 if (format == PixelFormat.OPAQUE) 911 format = PixelFormat.RGB_565; 912 913 mRequestedFormat = format; 914 if (mWindow != null) { 915 updateWindow(false, false); 916 } 917 } 918 919 /** 920 * @deprecated setType is now ignored. 921 */ 922 @Override 923 @Deprecated 924 public void setType(int type) { } 925 926 @Override 927 public void setKeepScreenOn(boolean screenOn) { 928 Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG); 929 msg.arg1 = screenOn ? 1 : 0; 930 mHandler.sendMessage(msg); 931 } 932 933 /** 934 * Gets a {@link Canvas} for drawing into the SurfaceView's Surface 935 * 936 * After drawing into the provided {@link Canvas}, the caller must 937 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. 938 * 939 * The caller must redraw the entire surface. 940 * @return A canvas for drawing into the surface. 941 */ 942 @Override 943 public Canvas lockCanvas() { 944 return internalLockCanvas(null); 945 } 946 947 /** 948 * Gets a {@link Canvas} for drawing into the SurfaceView's Surface 949 * 950 * After drawing into the provided {@link Canvas}, the caller must 951 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. 952 * 953 * @param inOutDirty A rectangle that represents the dirty region that the caller wants 954 * to redraw. This function may choose to expand the dirty rectangle if for example 955 * the surface has been resized or if the previous contents of the surface were 956 * not available. The caller must redraw the entire dirty region as represented 957 * by the contents of the inOutDirty rectangle upon return from this function. 958 * The caller may also pass <code>null</code> instead, in the case where the 959 * entire surface should be redrawn. 960 * @return A canvas for drawing into the surface. 961 */ 962 @Override 963 public Canvas lockCanvas(Rect inOutDirty) { 964 return internalLockCanvas(inOutDirty); 965 } 966 967 private final Canvas internalLockCanvas(Rect dirty) { 968 mSurfaceLock.lock(); 969 970 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped=" 971 + mDrawingStopped + ", win=" + mWindow); 972 973 Canvas c = null; 974 if (!mDrawingStopped && mWindow != null) { 975 try { 976 c = mSurface.lockCanvas(dirty); 977 } catch (Exception e) { 978 Log.e(LOG_TAG, "Exception locking surface", e); 979 } 980 } 981 982 if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Returned canvas: " + c); 983 if (c != null) { 984 mLastLockTime = SystemClock.uptimeMillis(); 985 return c; 986 } 987 988 // If the Surface is not ready to be drawn, then return null, 989 // but throttle calls to this function so it isn't called more 990 // than every 100ms. 991 long now = SystemClock.uptimeMillis(); 992 long nextTime = mLastLockTime + 100; 993 if (nextTime > now) { 994 try { 995 Thread.sleep(nextTime-now); 996 } catch (InterruptedException e) { 997 } 998 now = SystemClock.uptimeMillis(); 999 } 1000 mLastLockTime = now; 1001 mSurfaceLock.unlock(); 1002 1003 return null; 1004 } 1005 1006 /** 1007 * Posts the new contents of the {@link Canvas} to the surface and 1008 * releases the {@link Canvas}. 1009 * 1010 * @param canvas The canvas previously obtained from {@link #lockCanvas}. 1011 */ 1012 @Override 1013 public void unlockCanvasAndPost(Canvas canvas) { 1014 mSurface.unlockCanvasAndPost(canvas); 1015 mSurfaceLock.unlock(); 1016 } 1017 1018 @Override 1019 public Surface getSurface() { 1020 return mSurface; 1021 } 1022 1023 @Override 1024 public Rect getSurfaceFrame() { 1025 return mSurfaceFrame; 1026 } 1027 }; 1028 } 1029