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