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