Surface.java revision 2615de920be16de6c7fe49e85fc65923074c2041
1/* 2 * Copyright (C) 2007 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 android.annotation.IntDef; 20import android.content.res.CompatibilityInfo.Translator; 21import android.graphics.Canvas; 22import android.graphics.Matrix; 23import android.graphics.Rect; 24import android.graphics.SurfaceTexture; 25import android.os.Parcel; 26import android.os.Parcelable; 27import android.util.Log; 28 29import java.lang.annotation.Retention; 30import java.lang.annotation.RetentionPolicy; 31 32import dalvik.system.CloseGuard; 33 34/** 35 * Handle onto a raw buffer that is being managed by the screen compositor. 36 */ 37public class Surface implements Parcelable { 38 private static final String TAG = "Surface"; 39 40 private static native long nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture) 41 throws OutOfResourcesException; 42 private static native long nativeCreateFromSurfaceControl(long surfaceControlNativeObject); 43 44 private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty) 45 throws OutOfResourcesException; 46 private static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas); 47 48 private static native void nativeRelease(long nativeObject); 49 private static native boolean nativeIsValid(long nativeObject); 50 private static native boolean nativeIsConsumerRunningBehind(long nativeObject); 51 private static native long nativeReadFromParcel(long nativeObject, Parcel source); 52 private static native void nativeWriteToParcel(long nativeObject, Parcel dest); 53 54 private static native void nativeAllocateBuffers(long nativeObject); 55 56 public static final Parcelable.Creator<Surface> CREATOR = 57 new Parcelable.Creator<Surface>() { 58 @Override 59 public Surface createFromParcel(Parcel source) { 60 try { 61 Surface s = new Surface(); 62 s.readFromParcel(source); 63 return s; 64 } catch (Exception e) { 65 Log.e(TAG, "Exception creating surface from parcel", e); 66 return null; 67 } 68 } 69 70 @Override 71 public Surface[] newArray(int size) { 72 return new Surface[size]; 73 } 74 }; 75 76 private final CloseGuard mCloseGuard = CloseGuard.get(); 77 78 // Guarded state. 79 final Object mLock = new Object(); // protects the native state 80 private String mName; 81 long mNativeObject; // package scope only for SurfaceControl access 82 private long mLockedObject; 83 private int mGenerationId; // incremented each time mNativeObject changes 84 private final Canvas mCanvas = new CompatibleCanvas(); 85 86 // A matrix to scale the matrix set by application. This is set to null for 87 // non compatibility mode. 88 private Matrix mCompatibleMatrix; 89 90 private HwuiContext mHwuiContext; 91 92 /** @hide */ 93 @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) 94 @Retention(RetentionPolicy.SOURCE) 95 public @interface Rotation {} 96 97 /** 98 * Rotation constant: 0 degree rotation (natural orientation) 99 */ 100 public static final int ROTATION_0 = 0; 101 102 /** 103 * Rotation constant: 90 degree rotation. 104 */ 105 public static final int ROTATION_90 = 1; 106 107 /** 108 * Rotation constant: 180 degree rotation. 109 */ 110 public static final int ROTATION_180 = 2; 111 112 /** 113 * Rotation constant: 270 degree rotation. 114 */ 115 public static final int ROTATION_270 = 3; 116 117 /** 118 * Create an empty surface, which will later be filled in by readFromParcel(). 119 * @hide 120 */ 121 public Surface() { 122 } 123 124 /** 125 * Create Surface from a {@link SurfaceTexture}. 126 * 127 * Images drawn to the Surface will be made available to the {@link 128 * SurfaceTexture}, which can attach them to an OpenGL ES texture via {@link 129 * SurfaceTexture#updateTexImage}. 130 * 131 * @param surfaceTexture The {@link SurfaceTexture} that is updated by this 132 * Surface. 133 * @throws OutOfResourcesException if the surface could not be created. 134 */ 135 public Surface(SurfaceTexture surfaceTexture) { 136 if (surfaceTexture == null) { 137 throw new IllegalArgumentException("surfaceTexture must not be null"); 138 } 139 140 synchronized (mLock) { 141 mName = surfaceTexture.toString(); 142 setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture)); 143 } 144 } 145 146 /* called from android_view_Surface_createFromIGraphicBufferProducer() */ 147 private Surface(long nativeObject) { 148 synchronized (mLock) { 149 setNativeObjectLocked(nativeObject); 150 } 151 } 152 153 @Override 154 protected void finalize() throws Throwable { 155 try { 156 if (mCloseGuard != null) { 157 mCloseGuard.warnIfOpen(); 158 } 159 release(); 160 } finally { 161 super.finalize(); 162 } 163 } 164 165 /** 166 * Release the local reference to the server-side surface. 167 * Always call release() when you're done with a Surface. 168 * This will make the surface invalid. 169 */ 170 public void release() { 171 synchronized (mLock) { 172 if (mNativeObject != 0) { 173 nativeRelease(mNativeObject); 174 setNativeObjectLocked(0); 175 } 176 if (mHwuiContext != null) { 177 mHwuiContext.destroy(); 178 mHwuiContext = null; 179 } 180 } 181 } 182 183 /** 184 * Free all server-side state associated with this surface and 185 * release this object's reference. This method can only be 186 * called from the process that created the service. 187 * @hide 188 */ 189 public void destroy() { 190 release(); 191 } 192 193 /** 194 * Returns true if this object holds a valid surface. 195 * 196 * @return True if it holds a physical surface, so lockCanvas() will succeed. 197 * Otherwise returns false. 198 */ 199 public boolean isValid() { 200 synchronized (mLock) { 201 if (mNativeObject == 0) return false; 202 return nativeIsValid(mNativeObject); 203 } 204 } 205 206 /** 207 * Gets the generation number of this surface, incremented each time 208 * the native surface contained within this object changes. 209 * 210 * @return The current generation number. 211 * @hide 212 */ 213 public int getGenerationId() { 214 synchronized (mLock) { 215 return mGenerationId; 216 } 217 } 218 219 /** 220 * Returns true if the consumer of this Surface is running behind the producer. 221 * 222 * @return True if the consumer is more than one buffer ahead of the producer. 223 * @hide 224 */ 225 public boolean isConsumerRunningBehind() { 226 synchronized (mLock) { 227 checkNotReleasedLocked(); 228 return nativeIsConsumerRunningBehind(mNativeObject); 229 } 230 } 231 232 /** 233 * Gets a {@link Canvas} for drawing into this surface. 234 * 235 * After drawing into the provided {@link Canvas}, the caller must 236 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. 237 * 238 * @param inOutDirty A rectangle that represents the dirty region that the caller wants 239 * to redraw. This function may choose to expand the dirty rectangle if for example 240 * the surface has been resized or if the previous contents of the surface were 241 * not available. The caller must redraw the entire dirty region as represented 242 * by the contents of the inOutDirty rectangle upon return from this function. 243 * The caller may also pass <code>null</code> instead, in the case where the 244 * entire surface should be redrawn. 245 * @return A canvas for drawing into the surface. 246 * 247 * @throws IllegalArgumentException If the inOutDirty rectangle is not valid. 248 * @throws OutOfResourcesException If the canvas cannot be locked. 249 */ 250 public Canvas lockCanvas(Rect inOutDirty) 251 throws Surface.OutOfResourcesException, IllegalArgumentException { 252 synchronized (mLock) { 253 checkNotReleasedLocked(); 254 if (mLockedObject != 0) { 255 // Ideally, nativeLockCanvas() would throw in this situation and prevent the 256 // double-lock, but that won't happen if mNativeObject was updated. We can't 257 // abandon the old mLockedObject because it might still be in use, so instead 258 // we just refuse to re-lock the Surface. 259 throw new IllegalArgumentException("Surface was already locked"); 260 } 261 mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty); 262 return mCanvas; 263 } 264 } 265 266 /** 267 * Posts the new contents of the {@link Canvas} to the surface and 268 * releases the {@link Canvas}. 269 * 270 * @param canvas The canvas previously obtained from {@link #lockCanvas}. 271 */ 272 public void unlockCanvasAndPost(Canvas canvas) { 273 synchronized (mLock) { 274 checkNotReleasedLocked(); 275 276 if (mHwuiContext != null) { 277 mHwuiContext.unlockAndPost(canvas); 278 } else { 279 unlockSwCanvasAndPost(canvas); 280 } 281 } 282 } 283 284 private void unlockSwCanvasAndPost(Canvas canvas) { 285 if (canvas != mCanvas) { 286 throw new IllegalArgumentException("canvas object must be the same instance that " 287 + "was previously returned by lockCanvas"); 288 } 289 if (mNativeObject != mLockedObject) { 290 Log.w(TAG, "WARNING: Surface's mNativeObject (0x" + 291 Long.toHexString(mNativeObject) + ") != mLockedObject (0x" + 292 Long.toHexString(mLockedObject) +")"); 293 } 294 if (mLockedObject == 0) { 295 throw new IllegalStateException("Surface was not locked"); 296 } 297 try { 298 nativeUnlockCanvasAndPost(mLockedObject, canvas); 299 } finally { 300 nativeRelease(mLockedObject); 301 mLockedObject = 0; 302 } 303 } 304 305 /** 306 * Gets a {@link Canvas} for drawing into this surface. 307 * 308 * After drawing into the provided {@link Canvas}, the caller must 309 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. 310 * 311 * Unlike {@link #lockCanvas(Rect)} this will return a hardware-accelerated 312 * canvas. See the <a href="{@docRoot}guide/topics/graphics/hardware-accel.html#unsupported"> 313 * unsupported drawing operations</a> for a list of what is and isn't 314 * supported in a hardware-accelerated canvas. 315 * 316 * @return A canvas for drawing into the surface. 317 * 318 * @throws IllegalStateException If the canvas cannot be locked. 319 */ 320 public Canvas lockHardwareCanvas() { 321 synchronized (mLock) { 322 checkNotReleasedLocked(); 323 if (mHwuiContext == null) { 324 mHwuiContext = new HwuiContext(); 325 } 326 return mHwuiContext.lockCanvas(); 327 } 328 } 329 330 /** 331 * @deprecated This API has been removed and is not supported. Do not use. 332 */ 333 @Deprecated 334 public void unlockCanvas(Canvas canvas) { 335 throw new UnsupportedOperationException(); 336 } 337 338 /** 339 * Sets the translator used to scale canvas's width/height in compatibility 340 * mode. 341 */ 342 void setCompatibilityTranslator(Translator translator) { 343 if (translator != null) { 344 float appScale = translator.applicationScale; 345 mCompatibleMatrix = new Matrix(); 346 mCompatibleMatrix.setScale(appScale, appScale); 347 } 348 } 349 350 /** 351 * Copy another surface to this one. This surface now holds a reference 352 * to the same data as the original surface, and is -not- the owner. 353 * This is for use by the window manager when returning a window surface 354 * back from a client, converting it from the representation being managed 355 * by the window manager to the representation the client uses to draw 356 * in to it. 357 * @hide 358 */ 359 public void copyFrom(SurfaceControl other) { 360 if (other == null) { 361 throw new IllegalArgumentException("other must not be null"); 362 } 363 364 long surfaceControlPtr = other.mNativeObject; 365 if (surfaceControlPtr == 0) { 366 throw new NullPointerException( 367 "SurfaceControl native object is null. Are you using a released SurfaceControl?"); 368 } 369 long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr); 370 371 synchronized (mLock) { 372 if (mNativeObject != 0) { 373 nativeRelease(mNativeObject); 374 } 375 setNativeObjectLocked(newNativeObject); 376 } 377 } 378 379 /** 380 * This is intended to be used by {@link SurfaceView#updateWindow} only. 381 * @param other access is not thread safe 382 * @hide 383 * @deprecated 384 */ 385 @Deprecated 386 public void transferFrom(Surface other) { 387 if (other == null) { 388 throw new IllegalArgumentException("other must not be null"); 389 } 390 if (other != this) { 391 final long newPtr; 392 synchronized (other.mLock) { 393 newPtr = other.mNativeObject; 394 other.setNativeObjectLocked(0); 395 } 396 397 synchronized (mLock) { 398 if (mNativeObject != 0) { 399 nativeRelease(mNativeObject); 400 } 401 setNativeObjectLocked(newPtr); 402 } 403 } 404 } 405 406 @Override 407 public int describeContents() { 408 return 0; 409 } 410 411 public void readFromParcel(Parcel source) { 412 if (source == null) { 413 throw new IllegalArgumentException("source must not be null"); 414 } 415 416 synchronized (mLock) { 417 // nativeReadFromParcel() will either return mNativeObject, or 418 // create a new native Surface and return it after reducing 419 // the reference count on mNativeObject. Either way, it is 420 // not necessary to call nativeRelease() here. 421 mName = source.readString(); 422 setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source)); 423 } 424 } 425 426 @Override 427 public void writeToParcel(Parcel dest, int flags) { 428 if (dest == null) { 429 throw new IllegalArgumentException("dest must not be null"); 430 } 431 synchronized (mLock) { 432 dest.writeString(mName); 433 nativeWriteToParcel(mNativeObject, dest); 434 } 435 if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) { 436 release(); 437 } 438 } 439 440 @Override 441 public String toString() { 442 synchronized (mLock) { 443 return "Surface(name=" + mName + ")/@0x" + 444 Integer.toHexString(System.identityHashCode(this)); 445 } 446 } 447 448 private void setNativeObjectLocked(long ptr) { 449 if (mNativeObject != ptr) { 450 if (mNativeObject == 0 && ptr != 0) { 451 mCloseGuard.open("release"); 452 } else if (mNativeObject != 0 && ptr == 0) { 453 mCloseGuard.close(); 454 } 455 mNativeObject = ptr; 456 mGenerationId += 1; 457 if (mHwuiContext != null) { 458 mHwuiContext.updateSurface(); 459 } 460 } 461 } 462 463 private void checkNotReleasedLocked() { 464 if (mNativeObject == 0) { 465 throw new IllegalStateException("Surface has already been released."); 466 } 467 } 468 469 /** 470 * Allocate buffers ahead of time to avoid allocation delays during rendering 471 * @hide 472 */ 473 public void allocateBuffers() { 474 synchronized (mLock) { 475 checkNotReleasedLocked(); 476 nativeAllocateBuffers(mNativeObject); 477 } 478 } 479 480 /** 481 * Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or 482 * when a SurfaceTexture could not successfully be allocated. 483 */ 484 @SuppressWarnings("serial") 485 public static class OutOfResourcesException extends RuntimeException { 486 public OutOfResourcesException() { 487 } 488 public OutOfResourcesException(String name) { 489 super(name); 490 } 491 } 492 493 /** 494 * Returns a human readable representation of a rotation. 495 * 496 * @param rotation The rotation. 497 * @return The rotation symbolic name. 498 * 499 * @hide 500 */ 501 public static String rotationToString(int rotation) { 502 switch (rotation) { 503 case Surface.ROTATION_0: { 504 return "ROTATION_0"; 505 } 506 case Surface.ROTATION_90: { 507 return "ROATATION_90"; 508 } 509 case Surface.ROTATION_180: { 510 return "ROATATION_180"; 511 } 512 case Surface.ROTATION_270: { 513 return "ROATATION_270"; 514 } 515 default: { 516 throw new IllegalArgumentException("Invalid rotation: " + rotation); 517 } 518 } 519 } 520 521 /** 522 * A Canvas class that can handle the compatibility mode. 523 * This does two things differently. 524 * <ul> 525 * <li>Returns the width and height of the target metrics, rather than 526 * native. For example, the canvas returns 320x480 even if an app is running 527 * in WVGA high density. 528 * <li>Scales the matrix in setMatrix by the application scale, except if 529 * the matrix looks like obtained from getMatrix. This is a hack to handle 530 * the case that an application uses getMatrix to keep the original matrix, 531 * set matrix of its own, then set the original matrix back. There is no 532 * perfect solution that works for all cases, and there are a lot of cases 533 * that this model does not work, but we hope this works for many apps. 534 * </ul> 535 */ 536 private final class CompatibleCanvas extends Canvas { 537 // A temp matrix to remember what an application obtained via {@link getMatrix} 538 private Matrix mOrigMatrix = null; 539 540 @Override 541 public void setMatrix(Matrix matrix) { 542 if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) { 543 // don't scale the matrix if it's not compatibility mode, or 544 // the matrix was obtained from getMatrix. 545 super.setMatrix(matrix); 546 } else { 547 Matrix m = new Matrix(mCompatibleMatrix); 548 m.preConcat(matrix); 549 super.setMatrix(m); 550 } 551 } 552 553 @SuppressWarnings("deprecation") 554 @Override 555 public void getMatrix(Matrix m) { 556 super.getMatrix(m); 557 if (mOrigMatrix == null) { 558 mOrigMatrix = new Matrix(); 559 } 560 mOrigMatrix.set(m); 561 } 562 } 563 564 private final class HwuiContext { 565 private final RenderNode mRenderNode; 566 private long mHwuiRenderer; 567 private HardwareCanvas mCanvas; 568 569 HwuiContext() { 570 mRenderNode = RenderNode.create("HwuiCanvas", null); 571 mRenderNode.setClipToBounds(false); 572 mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject); 573 } 574 575 Canvas lockCanvas() { 576 if (mCanvas != null) { 577 throw new IllegalStateException("Surface was already locked!"); 578 } 579 mCanvas = mRenderNode.start(0, 0); 580 return mCanvas; 581 } 582 583 void unlockAndPost(Canvas canvas) { 584 if (canvas != mCanvas) { 585 throw new IllegalArgumentException("canvas object must be the same instance that " 586 + "was previously returned by lockCanvas"); 587 } 588 mRenderNode.end(mCanvas); 589 mCanvas = null; 590 nHwuiDraw(mHwuiRenderer); 591 } 592 593 void updateSurface() { 594 nHwuiSetSurface(mHwuiRenderer, mNativeObject); 595 } 596 597 void destroy() { 598 if (mHwuiRenderer != 0) { 599 nHwuiDestroy(mHwuiRenderer); 600 mHwuiRenderer = 0; 601 } 602 } 603 604 @Override 605 protected void finalize() throws Throwable { 606 try { 607 destroy(); 608 } finally { 609 super.finalize(); 610 } 611 } 612 } 613 614 private static native long nHwuiCreate(long rootNode, long surface); 615 private static native void nHwuiSetSurface(long renderer, long surface); 616 private static native void nHwuiDraw(long renderer); 617 private static native void nHwuiDestroy(long renderer); 618} 619