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