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