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