SurfaceView.java revision e36d6e277e49475076b7872d36ea6a5c5b996e9d
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 17package android.view; 18 19import com.android.internal.view.BaseIWindow; 20 21import android.content.Context; 22import android.content.res.Configuration; 23import android.content.res.Resources; 24import android.content.res.CompatibilityInfo.Translator; 25import android.graphics.Canvas; 26import android.graphics.PixelFormat; 27import android.graphics.PorterDuff; 28import android.graphics.Rect; 29import android.graphics.Region; 30import android.os.Handler; 31import android.os.Message; 32import android.os.RemoteException; 33import android.os.SystemClock; 34import android.os.ParcelFileDescriptor; 35import android.util.AttributeSet; 36import android.util.Config; 37import android.util.Log; 38 39import java.lang.ref.WeakReference; 40import java.util.ArrayList; 41import java.util.concurrent.locks.ReentrantLock; 42import java.lang.ref.WeakReference; 43 44/** 45 * Provides a dedicated drawing surface embedded inside of a view hierarchy. 46 * You can control the format of this surface and, if you like, its size; the 47 * SurfaceView takes care of placing the surface at the correct location on the 48 * screen 49 * 50 * <p>The surface is Z ordered so that it is behind the window holding its 51 * SurfaceView; the SurfaceView punches a hole in its window to allow its 52 * surface to be displayed. The view hierarchy will take care of correctly 53 * compositing with the Surface any siblings of the SurfaceView that would 54 * normally appear on top of it. This can be used to place overlays such as 55 * buttons on top of the Surface, though note however that it can have an 56 * impact on performance since a full alpha-blended composite will be performed 57 * each time the Surface changes. 58 * 59 * <p>Access to the underlying surface is provided via the SurfaceHolder interface, 60 * which can be retrieved by calling {@link #getHolder}. 61 * 62 * <p>The Surface will be created for you while the SurfaceView's window is 63 * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated} 64 * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the 65 * Surface is created and destroyed as the window is shown and hidden. 66 * 67 * <p>One of the purposes of this class is to provide a surface in which a 68 * secondary thread can render in to the screen. If you are going to use it 69 * this way, you need to be aware of some threading semantics: 70 * 71 * <ul> 72 * <li> All SurfaceView and 73 * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called 74 * from the thread running the SurfaceView's window (typically the main thread 75 * of the application). They thus need to correctly synchronize with any 76 * state that is also touched by the drawing thread. 77 * <li> You must ensure that the drawing thread only touches the underlying 78 * Surface while it is valid -- between 79 * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()} 80 * and 81 * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}. 82 * </ul> 83 */ 84public class SurfaceView extends View { 85 static private final String TAG = "SurfaceView"; 86 static private final boolean DEBUG = false; 87 static private final boolean localLOGV = DEBUG ? true : Config.LOGV; 88 89 final ArrayList<SurfaceHolder.Callback> mCallbacks 90 = new ArrayList<SurfaceHolder.Callback>(); 91 92 final int[] mLocation = new int[2]; 93 94 final ReentrantLock mSurfaceLock = new ReentrantLock(); 95 final Surface mSurface = new Surface(); 96 boolean mDrawingStopped = true; 97 98 final WindowManager.LayoutParams mLayout 99 = new WindowManager.LayoutParams(); 100 IWindowSession mSession; 101 MyWindow mWindow; 102 final Rect mVisibleInsets = new Rect(); 103 final Rect mWinFrame = new Rect(); 104 final Rect mContentInsets = new Rect(); 105 106 static final int KEEP_SCREEN_ON_MSG = 1; 107 static final int GET_NEW_SURFACE_MSG = 2; 108 109 int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; 110 111 boolean mIsCreating = false; 112 113 final Handler mHandler = new Handler() { 114 @Override 115 public void handleMessage(Message msg) { 116 switch (msg.what) { 117 case KEEP_SCREEN_ON_MSG: { 118 setKeepScreenOn(msg.arg1 != 0); 119 } break; 120 case GET_NEW_SURFACE_MSG: { 121 handleGetNewSurface(); 122 } break; 123 } 124 } 125 }; 126 127 boolean mRequestedVisible = false; 128 boolean mWindowVisibility = false; 129 boolean mViewVisibility = false; 130 int mRequestedWidth = -1; 131 int mRequestedHeight = -1; 132 int mRequestedFormat = PixelFormat.OPAQUE; 133 int mRequestedType = -1; 134 135 boolean mHaveFrame = false; 136 boolean mDestroyReportNeeded = false; 137 boolean mNewSurfaceNeeded = false; 138 long mLastLockTime = 0; 139 140 boolean mVisible = false; 141 int mLeft = -1; 142 int mTop = -1; 143 int mWidth = -1; 144 int mHeight = -1; 145 int mFormat = -1; 146 int mType = -1; 147 final Rect mSurfaceFrame = new Rect(); 148 private Translator mTranslator; 149 150 public SurfaceView(Context context) { 151 super(context); 152 setWillNotDraw(true); 153 } 154 155 public SurfaceView(Context context, AttributeSet attrs) { 156 super(context, attrs); 157 setWillNotDraw(true); 158 } 159 160 public SurfaceView(Context context, AttributeSet attrs, int defStyle) { 161 super(context, attrs, defStyle); 162 setWillNotDraw(true); 163 } 164 165 /** 166 * Return the SurfaceHolder providing access and control over this 167 * SurfaceView's underlying surface. 168 * 169 * @return SurfaceHolder The holder of the surface. 170 */ 171 public SurfaceHolder getHolder() { 172 return mSurfaceHolder; 173 } 174 175 @Override 176 protected void onAttachedToWindow() { 177 super.onAttachedToWindow(); 178 mParent.requestTransparentRegion(this); 179 mSession = getWindowSession(); 180 mLayout.token = getWindowToken(); 181 mLayout.setTitle("SurfaceView"); 182 mViewVisibility = getVisibility() == VISIBLE; 183 } 184 185 @Override 186 protected void onWindowVisibilityChanged(int visibility) { 187 super.onWindowVisibilityChanged(visibility); 188 mWindowVisibility = visibility == VISIBLE; 189 mRequestedVisible = mWindowVisibility && mViewVisibility; 190 updateWindow(false); 191 } 192 193 @Override 194 public void setVisibility(int visibility) { 195 super.setVisibility(visibility); 196 mViewVisibility = visibility == VISIBLE; 197 mRequestedVisible = mWindowVisibility && mViewVisibility; 198 updateWindow(false); 199 } 200 201 @Override 202 protected void onDetachedFromWindow() { 203 mRequestedVisible = false; 204 updateWindow(false); 205 mHaveFrame = false; 206 if (mWindow != null) { 207 try { 208 mSession.remove(mWindow); 209 } catch (RemoteException ex) { 210 } 211 mWindow = null; 212 } 213 mSession = null; 214 mLayout.token = null; 215 216 super.onDetachedFromWindow(); 217 } 218 219 @Override 220 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 221 int width = getDefaultSize(mRequestedWidth, widthMeasureSpec); 222 int height = getDefaultSize(mRequestedHeight, heightMeasureSpec); 223 setMeasuredDimension(width, height); 224 } 225 226 @Override 227 protected void onScrollChanged(int l, int t, int oldl, int oldt) { 228 super.onScrollChanged(l, t, oldl, oldt); 229 updateWindow(false); 230 } 231 232 @Override 233 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 234 super.onSizeChanged(w, h, oldw, oldh); 235 updateWindow(false); 236 } 237 238 @Override 239 public boolean gatherTransparentRegion(Region region) { 240 if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { 241 return super.gatherTransparentRegion(region); 242 } 243 244 boolean opaque = true; 245 if ((mPrivateFlags & SKIP_DRAW) == 0) { 246 // this view draws, remove it from the transparent region 247 opaque = super.gatherTransparentRegion(region); 248 } else if (region != null) { 249 int w = getWidth(); 250 int h = getHeight(); 251 if (w>0 && h>0) { 252 getLocationInWindow(mLocation); 253 // otherwise, punch a hole in the whole hierarchy 254 int l = mLocation[0]; 255 int t = mLocation[1]; 256 region.op(l, t, l+w, t+h, Region.Op.UNION); 257 } 258 } 259 if (PixelFormat.formatHasAlpha(mRequestedFormat)) { 260 opaque = false; 261 } 262 return opaque; 263 } 264 265 @Override 266 public void draw(Canvas canvas) { 267 if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { 268 // draw() is not called when SKIP_DRAW is set 269 if ((mPrivateFlags & SKIP_DRAW) == 0) { 270 // punch a whole in the view-hierarchy below us 271 canvas.drawColor(0, PorterDuff.Mode.CLEAR); 272 } 273 } 274 super.draw(canvas); 275 } 276 277 @Override 278 protected void dispatchDraw(Canvas canvas) { 279 if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { 280 // if SKIP_DRAW is cleared, draw() has already punched a hole 281 if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { 282 // punch a whole in the view-hierarchy below us 283 canvas.drawColor(0, PorterDuff.Mode.CLEAR); 284 } 285 } 286 // reposition ourselves where the surface is 287 mHaveFrame = true; 288 updateWindow(false); 289 super.dispatchDraw(canvas); 290 } 291 292 /** 293 * Control whether the surface view's surface is placed on top of another 294 * regular surface view in the window (but still behind the window itself). 295 * This is typically used to place overlays on top of an underlying media 296 * surface view. 297 * 298 * <p>Note that this must be set before the surface view's containing 299 * window is attached to the window manager. 300 * 301 * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}. 302 */ 303 public void setZOrderMediaOverlay(boolean isMediaOverlay) { 304 mWindowType = isMediaOverlay 305 ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY 306 : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; 307 } 308 309 /** 310 * Control whether the surface view's surface is placed on top of its 311 * window. Normally it is placed behind the window, to allow it to 312 * (for the most part) appear to composite with the views in the 313 * hierarchy. By setting this, you cause it to be placed above the 314 * window. This means that none of the contents of the window this 315 * SurfaceView is in will be visible on top of its surface. 316 * 317 * <p>Note that this must be set before the surface view's containing 318 * window is attached to the window manager. 319 * 320 * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}. 321 */ 322 public void setZOrderOnTop(boolean onTop) { 323 mWindowType = onTop ? WindowManager.LayoutParams.TYPE_APPLICATION_PANEL 324 : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; 325 } 326 327 /** 328 * Hack to allow special layering of windows. The type is one of the 329 * types in WindowManager.LayoutParams. This is a hack so: 330 * @hide 331 */ 332 public void setWindowType(int type) { 333 mWindowType = type; 334 } 335 336 private void updateWindow(boolean force) { 337 if (!mHaveFrame) { 338 return; 339 } 340 ViewRoot viewRoot = (ViewRoot) getRootView().getParent(); 341 if (viewRoot != null) { 342 mTranslator = viewRoot.mTranslator; 343 } 344 345 Resources res = getContext().getResources(); 346 if (mTranslator != null || !res.getCompatibilityInfo().supportsScreen()) { 347 mSurface.setCompatibleDisplayMetrics(res.getDisplayMetrics(), mTranslator); 348 } 349 350 int myWidth = mRequestedWidth; 351 if (myWidth <= 0) myWidth = getWidth(); 352 int myHeight = mRequestedHeight; 353 if (myHeight <= 0) myHeight = getHeight(); 354 355 getLocationInWindow(mLocation); 356 final boolean creating = mWindow == null; 357 final boolean formatChanged = mFormat != mRequestedFormat; 358 final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 359 final boolean visibleChanged = mVisible != mRequestedVisible 360 || mNewSurfaceNeeded; 361 final boolean typeChanged = mType != mRequestedType; 362 if (force || creating || formatChanged || sizeChanged || visibleChanged 363 || typeChanged || mLeft != mLocation[0] || mTop != mLocation[1]) { 364 365 if (localLOGV) Log.i(TAG, "Changes: creating=" + creating 366 + " format=" + formatChanged + " size=" + sizeChanged 367 + " visible=" + visibleChanged 368 + " left=" + (mLeft != mLocation[0]) 369 + " top=" + (mTop != mLocation[1])); 370 371 try { 372 final boolean visible = mVisible = mRequestedVisible; 373 mLeft = mLocation[0]; 374 mTop = mLocation[1]; 375 mWidth = myWidth; 376 mHeight = myHeight; 377 mFormat = mRequestedFormat; 378 mType = mRequestedType; 379 380 // Scaling/Translate window's layout here because mLayout is not used elsewhere. 381 382 // Places the window relative 383 mLayout.x = mLeft; 384 mLayout.y = mTop; 385 mLayout.width = getWidth(); 386 mLayout.height = getHeight(); 387 if (mTranslator != null) { 388 mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout); 389 } 390 391 mLayout.format = mRequestedFormat; 392 mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 393 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 394 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 395 | WindowManager.LayoutParams.FLAG_SCALED 396 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 397 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 398 ; 399 if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) { 400 mLayout.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; 401 } 402 403 mLayout.memoryType = mRequestedType; 404 405 if (mWindow == null) { 406 mWindow = new MyWindow(this); 407 mLayout.type = mWindowType; 408 mLayout.gravity = Gravity.LEFT|Gravity.TOP; 409 mSession.add(mWindow, mLayout, 410 mVisible ? VISIBLE : GONE, mContentInsets); 411 } 412 413 if (visibleChanged && (!visible || mNewSurfaceNeeded)) { 414 reportSurfaceDestroyed(); 415 } 416 417 mNewSurfaceNeeded = false; 418 419 mSurfaceLock.lock(); 420 mDrawingStopped = !visible; 421 422 final int relayoutResult = mSession.relayout( 423 mWindow, mLayout, mWidth, mHeight, 424 visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets, 425 mVisibleInsets, mSurface); 426 427 if (localLOGV) Log.i(TAG, "New surface: " + mSurface 428 + ", vis=" + visible + ", frame=" + mWinFrame); 429 430 mSurfaceFrame.left = 0; 431 mSurfaceFrame.top = 0; 432 if (mTranslator == null) { 433 mSurfaceFrame.right = mWinFrame.width(); 434 mSurfaceFrame.bottom = mWinFrame.height(); 435 } else { 436 float appInvertedScale = mTranslator.applicationInvertedScale; 437 mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f); 438 mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f); 439 } 440 mSurfaceLock.unlock(); 441 442 try { 443 if (visible) { 444 mDestroyReportNeeded = true; 445 446 SurfaceHolder.Callback callbacks[]; 447 synchronized (mCallbacks) { 448 callbacks = new SurfaceHolder.Callback[mCallbacks.size()]; 449 mCallbacks.toArray(callbacks); 450 } 451 452 if (visibleChanged) { 453 mIsCreating = true; 454 for (SurfaceHolder.Callback c : callbacks) { 455 c.surfaceCreated(mSurfaceHolder); 456 } 457 } 458 if (creating || formatChanged || sizeChanged 459 || visibleChanged) { 460 for (SurfaceHolder.Callback c : callbacks) { 461 c.surfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight); 462 } 463 } 464 } 465 } finally { 466 mIsCreating = false; 467 if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { 468 mSession.finishDrawing(mWindow); 469 } 470 } 471 } catch (RemoteException ex) { 472 } 473 if (localLOGV) Log.v( 474 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 475 " w=" + mLayout.width + " h=" + mLayout.height + 476 ", frame=" + mSurfaceFrame); 477 } 478 } 479 480 private void reportSurfaceDestroyed() { 481 if (mDestroyReportNeeded) { 482 mDestroyReportNeeded = false; 483 SurfaceHolder.Callback callbacks[]; 484 synchronized (mCallbacks) { 485 callbacks = new SurfaceHolder.Callback[mCallbacks.size()]; 486 mCallbacks.toArray(callbacks); 487 } 488 for (SurfaceHolder.Callback c : callbacks) { 489 c.surfaceDestroyed(mSurfaceHolder); 490 } 491 } 492 super.onDetachedFromWindow(); 493 } 494 495 void handleGetNewSurface() { 496 mNewSurfaceNeeded = true; 497 updateWindow(false); 498 } 499 500 private static class MyWindow extends BaseIWindow { 501 private final WeakReference<SurfaceView> mSurfaceView; 502 503 public MyWindow(SurfaceView surfaceView) { 504 mSurfaceView = new WeakReference<SurfaceView>(surfaceView); 505 } 506 507 public void resized(int w, int h, Rect coveredInsets, 508 Rect visibleInsets, boolean reportDraw, Configuration newConfig) { 509 SurfaceView surfaceView = mSurfaceView.get(); 510 if (surfaceView != null) { 511 if (localLOGV) Log.v( 512 "SurfaceView", surfaceView + " got resized: w=" + 513 w + " h=" + h + ", cur w=" + mCurWidth + " h=" + mCurHeight); 514 synchronized (this) { 515 if (mCurWidth != w || mCurHeight != h) { 516 mCurWidth = w; 517 mCurHeight = h; 518 } 519 if (reportDraw) { 520 try { 521 surfaceView.mSession.finishDrawing(surfaceView.mWindow); 522 } catch (RemoteException e) { 523 } 524 } 525 } 526 } 527 } 528 529 public void dispatchKey(KeyEvent event) { 530 SurfaceView surfaceView = mSurfaceView.get(); 531 if (surfaceView != null) { 532 //Log.w("SurfaceView", "Unexpected key event in surface: " + event); 533 if (surfaceView.mSession != null && surfaceView.mSurface != null) { 534 try { 535 surfaceView.mSession.finishKey(surfaceView.mWindow); 536 } catch (RemoteException ex) { 537 } 538 } 539 } 540 } 541 542 public void dispatchPointer(MotionEvent event, long eventTime, 543 boolean callWhenDone) { 544 Log.w("SurfaceView", "Unexpected pointer event in surface: " + event); 545 //if (mSession != null && mSurface != null) { 546 // try { 547 // //mSession.finishKey(mWindow); 548 // } catch (RemoteException ex) { 549 // } 550 //} 551 } 552 553 public void dispatchTrackball(MotionEvent event, long eventTime, 554 boolean callWhenDone) { 555 Log.w("SurfaceView", "Unexpected trackball event in surface: " + event); 556 //if (mSession != null && mSurface != null) { 557 // try { 558 // //mSession.finishKey(mWindow); 559 // } catch (RemoteException ex) { 560 // } 561 //} 562 } 563 564 public void dispatchAppVisibility(boolean visible) { 565 // The point of SurfaceView is to let the app control the surface. 566 } 567 568 public void dispatchGetNewSurface() { 569 SurfaceView surfaceView = mSurfaceView.get(); 570 if (surfaceView != null) { 571 Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG); 572 surfaceView.mHandler.sendMessage(msg); 573 } 574 } 575 576 public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) { 577 Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled); 578 } 579 580 public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { 581 } 582 583 int mCurWidth = -1; 584 int mCurHeight = -1; 585 } 586 587 private SurfaceHolder mSurfaceHolder = new SurfaceHolder() { 588 589 private static final String LOG_TAG = "SurfaceHolder"; 590 private int mSaveCount; 591 592 public boolean isCreating() { 593 return mIsCreating; 594 } 595 596 public void addCallback(Callback callback) { 597 synchronized (mCallbacks) { 598 // This is a linear search, but in practice we'll 599 // have only a couple callbacks, so it doesn't matter. 600 if (mCallbacks.contains(callback) == false) { 601 mCallbacks.add(callback); 602 } 603 } 604 } 605 606 public void removeCallback(Callback callback) { 607 synchronized (mCallbacks) { 608 mCallbacks.remove(callback); 609 } 610 } 611 612 public void setFixedSize(int width, int height) { 613 if (mRequestedWidth != width || mRequestedHeight != height) { 614 mRequestedWidth = width; 615 mRequestedHeight = height; 616 requestLayout(); 617 } 618 } 619 620 public void setSizeFromLayout() { 621 if (mRequestedWidth != -1 || mRequestedHeight != -1) { 622 mRequestedWidth = mRequestedHeight = -1; 623 requestLayout(); 624 } 625 } 626 627 public void setFormat(int format) { 628 mRequestedFormat = format; 629 if (mWindow != null) { 630 updateWindow(false); 631 } 632 } 633 634 public void setType(int type) { 635 switch (type) { 636 case SURFACE_TYPE_HARDWARE: 637 case SURFACE_TYPE_GPU: 638 // these are deprecated, treat as "NORMAL" 639 type = SURFACE_TYPE_NORMAL; 640 break; 641 } 642 switch (type) { 643 case SURFACE_TYPE_NORMAL: 644 case SURFACE_TYPE_PUSH_BUFFERS: 645 mRequestedType = type; 646 if (mWindow != null) { 647 updateWindow(false); 648 } 649 break; 650 } 651 } 652 653 public void setKeepScreenOn(boolean screenOn) { 654 Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG); 655 msg.arg1 = screenOn ? 1 : 0; 656 mHandler.sendMessage(msg); 657 } 658 659 public Canvas lockCanvas() { 660 return internalLockCanvas(null); 661 } 662 663 public Canvas lockCanvas(Rect dirty) { 664 return internalLockCanvas(dirty); 665 } 666 667 private final Canvas internalLockCanvas(Rect dirty) { 668 if (mType == SURFACE_TYPE_PUSH_BUFFERS) { 669 throw new BadSurfaceTypeException( 670 "Surface type is SURFACE_TYPE_PUSH_BUFFERS"); 671 } 672 mSurfaceLock.lock(); 673 674 if (localLOGV) Log.i(TAG, "Locking canvas... stopped=" 675 + mDrawingStopped + ", win=" + mWindow); 676 677 Canvas c = null; 678 if (!mDrawingStopped && mWindow != null) { 679 Rect frame = dirty != null ? dirty : mSurfaceFrame; 680 try { 681 c = mSurface.lockCanvas(frame); 682 } catch (Exception e) { 683 Log.e(LOG_TAG, "Exception locking surface", e); 684 } 685 } 686 687 if (localLOGV) Log.i(TAG, "Returned canvas: " + c); 688 if (c != null) { 689 mLastLockTime = SystemClock.uptimeMillis(); 690 return c; 691 } 692 693 // If the Surface is not ready to be drawn, then return null, 694 // but throttle calls to this function so it isn't called more 695 // than every 100ms. 696 long now = SystemClock.uptimeMillis(); 697 long nextTime = mLastLockTime + 100; 698 if (nextTime > now) { 699 try { 700 Thread.sleep(nextTime-now); 701 } catch (InterruptedException e) { 702 } 703 now = SystemClock.uptimeMillis(); 704 } 705 mLastLockTime = now; 706 mSurfaceLock.unlock(); 707 708 return null; 709 } 710 711 public void unlockCanvasAndPost(Canvas canvas) { 712 mSurface.unlockCanvasAndPost(canvas); 713 mSurfaceLock.unlock(); 714 } 715 716 public Surface getSurface() { 717 return mSurface; 718 } 719 720 public Rect getSurfaceFrame() { 721 return mSurfaceFrame; 722 } 723 }; 724} 725