Surface.java revision c298f9c9170b9e8721cfb616f784ec22734f22f4
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. It is also required to 315 * fully cover the surface every time {@link #lockHardwareCanvas()} is 316 * called as the buffer is not preserved between frames. Partial updates 317 * are not supported. 318 * 319 * @return A canvas for drawing into the surface. 320 * 321 * @throws IllegalStateException If the canvas cannot be locked. 322 */ 323 public Canvas lockHardwareCanvas() { 324 synchronized (mLock) { 325 checkNotReleasedLocked(); 326 if (mHwuiContext == null) { 327 mHwuiContext = new HwuiContext(); 328 } 329 return mHwuiContext.lockCanvas(); 330 } 331 } 332 333 /** 334 * @deprecated This API has been removed and is not supported. Do not use. 335 */ 336 @Deprecated 337 public void unlockCanvas(Canvas canvas) { 338 throw new UnsupportedOperationException(); 339 } 340 341 /** 342 * Sets the translator used to scale canvas's width/height in compatibility 343 * mode. 344 */ 345 void setCompatibilityTranslator(Translator translator) { 346 if (translator != null) { 347 float appScale = translator.applicationScale; 348 mCompatibleMatrix = new Matrix(); 349 mCompatibleMatrix.setScale(appScale, appScale); 350 } 351 } 352 353 /** 354 * Copy another surface to this one. This surface now holds a reference 355 * to the same data as the original surface, and is -not- the owner. 356 * This is for use by the window manager when returning a window surface 357 * back from a client, converting it from the representation being managed 358 * by the window manager to the representation the client uses to draw 359 * in to it. 360 * @hide 361 */ 362 public void copyFrom(SurfaceControl other) { 363 if (other == null) { 364 throw new IllegalArgumentException("other must not be null"); 365 } 366 367 long surfaceControlPtr = other.mNativeObject; 368 if (surfaceControlPtr == 0) { 369 throw new NullPointerException( 370 "SurfaceControl native object is null. Are you using a released SurfaceControl?"); 371 } 372 long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr); 373 374 synchronized (mLock) { 375 if (mNativeObject != 0) { 376 nativeRelease(mNativeObject); 377 } 378 setNativeObjectLocked(newNativeObject); 379 } 380 } 381 382 /** 383 * This is intended to be used by {@link SurfaceView#updateWindow} only. 384 * @param other access is not thread safe 385 * @hide 386 * @deprecated 387 */ 388 @Deprecated 389 public void transferFrom(Surface other) { 390 if (other == null) { 391 throw new IllegalArgumentException("other must not be null"); 392 } 393 if (other != this) { 394 final long newPtr; 395 synchronized (other.mLock) { 396 newPtr = other.mNativeObject; 397 other.setNativeObjectLocked(0); 398 } 399 400 synchronized (mLock) { 401 if (mNativeObject != 0) { 402 nativeRelease(mNativeObject); 403 } 404 setNativeObjectLocked(newPtr); 405 } 406 } 407 } 408 409 @Override 410 public int describeContents() { 411 return 0; 412 } 413 414 public void readFromParcel(Parcel source) { 415 if (source == null) { 416 throw new IllegalArgumentException("source must not be null"); 417 } 418 419 synchronized (mLock) { 420 // nativeReadFromParcel() will either return mNativeObject, or 421 // create a new native Surface and return it after reducing 422 // the reference count on mNativeObject. Either way, it is 423 // not necessary to call nativeRelease() here. 424 mName = source.readString(); 425 setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source)); 426 } 427 } 428 429 @Override 430 public void writeToParcel(Parcel dest, int flags) { 431 if (dest == null) { 432 throw new IllegalArgumentException("dest must not be null"); 433 } 434 synchronized (mLock) { 435 dest.writeString(mName); 436 nativeWriteToParcel(mNativeObject, dest); 437 } 438 if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) { 439 release(); 440 } 441 } 442 443 @Override 444 public String toString() { 445 synchronized (mLock) { 446 return "Surface(name=" + mName + ")/@0x" + 447 Integer.toHexString(System.identityHashCode(this)); 448 } 449 } 450 451 private void setNativeObjectLocked(long ptr) { 452 if (mNativeObject != ptr) { 453 if (mNativeObject == 0 && ptr != 0) { 454 mCloseGuard.open("release"); 455 } else if (mNativeObject != 0 && ptr == 0) { 456 mCloseGuard.close(); 457 } 458 mNativeObject = ptr; 459 mGenerationId += 1; 460 if (mHwuiContext != null) { 461 mHwuiContext.updateSurface(); 462 } 463 } 464 } 465 466 private void checkNotReleasedLocked() { 467 if (mNativeObject == 0) { 468 throw new IllegalStateException("Surface has already been released."); 469 } 470 } 471 472 /** 473 * Allocate buffers ahead of time to avoid allocation delays during rendering 474 * @hide 475 */ 476 public void allocateBuffers() { 477 synchronized (mLock) { 478 checkNotReleasedLocked(); 479 nativeAllocateBuffers(mNativeObject); 480 } 481 } 482 483 /** 484 * Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or 485 * when a SurfaceTexture could not successfully be allocated. 486 */ 487 @SuppressWarnings("serial") 488 public static class OutOfResourcesException extends RuntimeException { 489 public OutOfResourcesException() { 490 } 491 public OutOfResourcesException(String name) { 492 super(name); 493 } 494 } 495 496 /** 497 * Returns a human readable representation of a rotation. 498 * 499 * @param rotation The rotation. 500 * @return The rotation symbolic name. 501 * 502 * @hide 503 */ 504 public static String rotationToString(int rotation) { 505 switch (rotation) { 506 case Surface.ROTATION_0: { 507 return "ROTATION_0"; 508 } 509 case Surface.ROTATION_90: { 510 return "ROATATION_90"; 511 } 512 case Surface.ROTATION_180: { 513 return "ROATATION_180"; 514 } 515 case Surface.ROTATION_270: { 516 return "ROATATION_270"; 517 } 518 default: { 519 throw new IllegalArgumentException("Invalid rotation: " + rotation); 520 } 521 } 522 } 523 524 /** 525 * A Canvas class that can handle the compatibility mode. 526 * This does two things differently. 527 * <ul> 528 * <li>Returns the width and height of the target metrics, rather than 529 * native. For example, the canvas returns 320x480 even if an app is running 530 * in WVGA high density. 531 * <li>Scales the matrix in setMatrix by the application scale, except if 532 * the matrix looks like obtained from getMatrix. This is a hack to handle 533 * the case that an application uses getMatrix to keep the original matrix, 534 * set matrix of its own, then set the original matrix back. There is no 535 * perfect solution that works for all cases, and there are a lot of cases 536 * that this model does not work, but we hope this works for many apps. 537 * </ul> 538 */ 539 private final class CompatibleCanvas extends Canvas { 540 // A temp matrix to remember what an application obtained via {@link getMatrix} 541 private Matrix mOrigMatrix = null; 542 543 @Override 544 public void setMatrix(Matrix matrix) { 545 if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) { 546 // don't scale the matrix if it's not compatibility mode, or 547 // the matrix was obtained from getMatrix. 548 super.setMatrix(matrix); 549 } else { 550 Matrix m = new Matrix(mCompatibleMatrix); 551 m.preConcat(matrix); 552 super.setMatrix(m); 553 } 554 } 555 556 @SuppressWarnings("deprecation") 557 @Override 558 public void getMatrix(Matrix m) { 559 super.getMatrix(m); 560 if (mOrigMatrix == null) { 561 mOrigMatrix = new Matrix(); 562 } 563 mOrigMatrix.set(m); 564 } 565 } 566 567 private final class HwuiContext { 568 private final RenderNode mRenderNode; 569 private long mHwuiRenderer; 570 private HardwareCanvas mCanvas; 571 572 HwuiContext() { 573 mRenderNode = RenderNode.create("HwuiCanvas", null); 574 mRenderNode.setClipToBounds(false); 575 mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject); 576 } 577 578 Canvas lockCanvas() { 579 if (mCanvas != null) { 580 throw new IllegalStateException("Surface was already locked!"); 581 } 582 mCanvas = mRenderNode.start(0, 0); 583 return mCanvas; 584 } 585 586 void unlockAndPost(Canvas canvas) { 587 if (canvas != mCanvas) { 588 throw new IllegalArgumentException("canvas object must be the same instance that " 589 + "was previously returned by lockCanvas"); 590 } 591 mRenderNode.end(mCanvas); 592 mCanvas = null; 593 nHwuiDraw(mHwuiRenderer); 594 } 595 596 void updateSurface() { 597 nHwuiSetSurface(mHwuiRenderer, mNativeObject); 598 } 599 600 void destroy() { 601 if (mHwuiRenderer != 0) { 602 nHwuiDestroy(mHwuiRenderer); 603 mHwuiRenderer = 0; 604 } 605 } 606 } 607 608 private static native long nHwuiCreate(long rootNode, long surface); 609 private static native void nHwuiSetSurface(long renderer, long surface); 610 private static native void nHwuiDraw(long renderer); 611 private static native void nHwuiDestroy(long renderer); 612} 613