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 SurfaceView(Context context)195 public SurfaceView(Context context) { 196 super(context); 197 init(); 198 } 199 SurfaceView(Context context, AttributeSet attrs)200 public SurfaceView(Context context, AttributeSet attrs) { 201 super(context, attrs); 202 init(); 203 } 204 SurfaceView(Context context, AttributeSet attrs, int defStyleAttr)205 public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { 206 super(context, attrs, defStyleAttr); 207 init(); 208 } 209 SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)210 public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 211 super(context, attrs, defStyleAttr, defStyleRes); 212 init(); 213 } 214 init()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 */ getHolder()225 public SurfaceHolder getHolder() { 226 return mSurfaceHolder; 227 } 228 229 @Override onAttachedToWindow()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 onWindowVisibilityChanged(int visibility)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 setVisibility(int visibility)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 onDetachedFromWindow()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 onMeasure(int widthMeasureSpec, int heightMeasureSpec)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 setFrame(int left, int top, int right, int bottom)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 gatherTransparentRegion(Region region)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 draw(Canvas canvas)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 dispatchDraw(Canvas canvas)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 */ setZOrderMediaOverlay(boolean isMediaOverlay)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 */ setZOrderOnTop(boolean onTop)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 */ setSecure(boolean isSecure)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 */ setWindowType(int type)434 public void setWindowType(int type) { 435 mWindowType = type; 436 } 437 438 /** @hide */ updateWindow(boolean force, boolean redrawNeeded)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 */ updateWindowPositionRT(long frameNumber, int left, int top, int right, int bottom)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 */ windowPositionLostRT(long frameNumber)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 getSurfaceCallbacks()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 handleGetNewSurface()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 */ isFixedSize()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 MyWindow(SurfaceView surfaceView)803 public MyWindow(SurfaceView surfaceView) { 804 mSurfaceView = new WeakReference<SurfaceView>(surfaceView); 805 } 806 807 @Override resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig, Rect backDropRect, boolean forceLayout, boolean alwaysConsumeNavBar)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 dispatchAppVisibility(boolean visible)835 public void dispatchAppVisibility(boolean visible) { 836 // The point of SurfaceView is to let the app control the surface. 837 } 838 839 @Override dispatchGetNewSurface()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 windowFocusChanged(boolean hasFocus, boolean touchEnabled)849 public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) { 850 Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled); 851 } 852 853 @Override executeCommand(String command, String parameters, ParcelFileDescriptor out)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