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