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 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 /** @hide */ 91 @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) 92 @Retention(RetentionPolicy.SOURCE) 93 public @interface Rotation {} 94 95 /** 96 * Rotation constant: 0 degree rotation (natural orientation) 97 */ 98 public static final int ROTATION_0 = 0; 99 100 /** 101 * Rotation constant: 90 degree rotation. 102 */ 103 public static final int ROTATION_90 = 1; 104 105 /** 106 * Rotation constant: 180 degree rotation. 107 */ 108 public static final int ROTATION_180 = 2; 109 110 /** 111 * Rotation constant: 270 degree rotation. 112 */ 113 public static final int ROTATION_270 = 3; 114 115 /** 116 * Create an empty surface, which will later be filled in by readFromParcel(). 117 * @hide 118 */ Surface()119 public Surface() { 120 } 121 122 /** 123 * Create Surface from a {@link SurfaceTexture}. 124 * 125 * Images drawn to the Surface will be made available to the {@link 126 * SurfaceTexture}, which can attach them to an OpenGL ES texture via {@link 127 * SurfaceTexture#updateTexImage}. 128 * 129 * @param surfaceTexture The {@link SurfaceTexture} that is updated by this 130 * Surface. 131 * @throws OutOfResourcesException if the surface could not be created. 132 */ Surface(SurfaceTexture surfaceTexture)133 public Surface(SurfaceTexture surfaceTexture) { 134 if (surfaceTexture == null) { 135 throw new IllegalArgumentException("surfaceTexture must not be null"); 136 } 137 138 synchronized (mLock) { 139 mName = surfaceTexture.toString(); 140 setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture)); 141 } 142 } 143 144 /* called from android_view_Surface_createFromIGraphicBufferProducer() */ Surface(long nativeObject)145 private Surface(long nativeObject) { 146 synchronized (mLock) { 147 setNativeObjectLocked(nativeObject); 148 } 149 } 150 151 @Override finalize()152 protected void finalize() throws Throwable { 153 try { 154 if (mCloseGuard != null) { 155 mCloseGuard.warnIfOpen(); 156 } 157 release(); 158 } finally { 159 super.finalize(); 160 } 161 } 162 163 /** 164 * Release the local reference to the server-side surface. 165 * Always call release() when you're done with a Surface. 166 * This will make the surface invalid. 167 */ release()168 public void release() { 169 synchronized (mLock) { 170 if (mNativeObject != 0) { 171 nativeRelease(mNativeObject); 172 setNativeObjectLocked(0); 173 } 174 } 175 } 176 177 /** 178 * Free all server-side state associated with this surface and 179 * release this object's reference. This method can only be 180 * called from the process that created the service. 181 * @hide 182 */ destroy()183 public void destroy() { 184 release(); 185 } 186 187 /** 188 * Returns true if this object holds a valid surface. 189 * 190 * @return True if it holds a physical surface, so lockCanvas() will succeed. 191 * Otherwise returns false. 192 */ isValid()193 public boolean isValid() { 194 synchronized (mLock) { 195 if (mNativeObject == 0) return false; 196 return nativeIsValid(mNativeObject); 197 } 198 } 199 200 /** 201 * Gets the generation number of this surface, incremented each time 202 * the native surface contained within this object changes. 203 * 204 * @return The current generation number. 205 * @hide 206 */ getGenerationId()207 public int getGenerationId() { 208 synchronized (mLock) { 209 return mGenerationId; 210 } 211 } 212 213 /** 214 * Returns true if the consumer of this Surface is running behind the producer. 215 * 216 * @return True if the consumer is more than one buffer ahead of the producer. 217 * @hide 218 */ isConsumerRunningBehind()219 public boolean isConsumerRunningBehind() { 220 synchronized (mLock) { 221 checkNotReleasedLocked(); 222 return nativeIsConsumerRunningBehind(mNativeObject); 223 } 224 } 225 226 /** 227 * Gets a {@link Canvas} for drawing into this surface. 228 * 229 * After drawing into the provided {@link Canvas}, the caller must 230 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. 231 * 232 * @param inOutDirty A rectangle that represents the dirty region that the caller wants 233 * to redraw. This function may choose to expand the dirty rectangle if for example 234 * the surface has been resized or if the previous contents of the surface were 235 * not available. The caller must redraw the entire dirty region as represented 236 * by the contents of the inOutDirty rectangle upon return from this function. 237 * The caller may also pass <code>null</code> instead, in the case where the 238 * entire surface should be redrawn. 239 * @return A canvas for drawing into the surface. 240 * 241 * @throws IllegalArgumentException If the inOutDirty rectangle is not valid. 242 * @throws OutOfResourcesException If the canvas cannot be locked. 243 */ lockCanvas(Rect inOutDirty)244 public Canvas lockCanvas(Rect inOutDirty) 245 throws Surface.OutOfResourcesException, IllegalArgumentException { 246 synchronized (mLock) { 247 checkNotReleasedLocked(); 248 if (mLockedObject != 0) { 249 // Ideally, nativeLockCanvas() would throw in this situation and prevent the 250 // double-lock, but that won't happen if mNativeObject was updated. We can't 251 // abandon the old mLockedObject because it might still be in use, so instead 252 // we just refuse to re-lock the Surface. 253 throw new IllegalArgumentException("Surface was already locked"); 254 } 255 mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty); 256 return mCanvas; 257 } 258 } 259 260 /** 261 * Posts the new contents of the {@link Canvas} to the surface and 262 * releases the {@link Canvas}. 263 * 264 * @param canvas The canvas previously obtained from {@link #lockCanvas}. 265 */ unlockCanvasAndPost(Canvas canvas)266 public void unlockCanvasAndPost(Canvas canvas) { 267 if (canvas != mCanvas) { 268 throw new IllegalArgumentException("canvas object must be the same instance that " 269 + "was previously returned by lockCanvas"); 270 } 271 272 synchronized (mLock) { 273 checkNotReleasedLocked(); 274 if (mNativeObject != mLockedObject) { 275 Log.w(TAG, "WARNING: Surface's mNativeObject (0x" + 276 Long.toHexString(mNativeObject) + ") != mLockedObject (0x" + 277 Long.toHexString(mLockedObject) +")"); 278 } 279 if (mLockedObject == 0) { 280 throw new IllegalStateException("Surface was not locked"); 281 } 282 try { 283 nativeUnlockCanvasAndPost(mLockedObject, canvas); 284 } finally { 285 nativeRelease(mLockedObject); 286 mLockedObject = 0; 287 } 288 } 289 } 290 291 /** 292 * @deprecated This API has been removed and is not supported. Do not use. 293 */ 294 @Deprecated unlockCanvas(Canvas canvas)295 public void unlockCanvas(Canvas canvas) { 296 throw new UnsupportedOperationException(); 297 } 298 299 /** 300 * Sets the translator used to scale canvas's width/height in compatibility 301 * mode. 302 */ setCompatibilityTranslator(Translator translator)303 void setCompatibilityTranslator(Translator translator) { 304 if (translator != null) { 305 float appScale = translator.applicationScale; 306 mCompatibleMatrix = new Matrix(); 307 mCompatibleMatrix.setScale(appScale, appScale); 308 } 309 } 310 311 /** 312 * Copy another surface to this one. This surface now holds a reference 313 * to the same data as the original surface, and is -not- the owner. 314 * This is for use by the window manager when returning a window surface 315 * back from a client, converting it from the representation being managed 316 * by the window manager to the representation the client uses to draw 317 * in to it. 318 * @hide 319 */ copyFrom(SurfaceControl other)320 public void copyFrom(SurfaceControl other) { 321 if (other == null) { 322 throw new IllegalArgumentException("other must not be null"); 323 } 324 325 long surfaceControlPtr = other.mNativeObject; 326 if (surfaceControlPtr == 0) { 327 throw new NullPointerException( 328 "SurfaceControl native object is null. Are you using a released SurfaceControl?"); 329 } 330 long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr); 331 332 synchronized (mLock) { 333 if (mNativeObject != 0) { 334 nativeRelease(mNativeObject); 335 } 336 setNativeObjectLocked(newNativeObject); 337 } 338 } 339 340 /** 341 * This is intended to be used by {@link SurfaceView#updateWindow} only. 342 * @param other access is not thread safe 343 * @hide 344 * @deprecated 345 */ 346 @Deprecated transferFrom(Surface other)347 public void transferFrom(Surface other) { 348 if (other == null) { 349 throw new IllegalArgumentException("other must not be null"); 350 } 351 if (other != this) { 352 final long newPtr; 353 synchronized (other.mLock) { 354 newPtr = other.mNativeObject; 355 other.setNativeObjectLocked(0); 356 } 357 358 synchronized (mLock) { 359 if (mNativeObject != 0) { 360 nativeRelease(mNativeObject); 361 } 362 setNativeObjectLocked(newPtr); 363 } 364 } 365 } 366 367 @Override describeContents()368 public int describeContents() { 369 return 0; 370 } 371 readFromParcel(Parcel source)372 public void readFromParcel(Parcel source) { 373 if (source == null) { 374 throw new IllegalArgumentException("source must not be null"); 375 } 376 377 synchronized (mLock) { 378 // nativeReadFromParcel() will either return mNativeObject, or 379 // create a new native Surface and return it after reducing 380 // the reference count on mNativeObject. Either way, it is 381 // not necessary to call nativeRelease() here. 382 mName = source.readString(); 383 setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source)); 384 } 385 } 386 387 @Override writeToParcel(Parcel dest, int flags)388 public void writeToParcel(Parcel dest, int flags) { 389 if (dest == null) { 390 throw new IllegalArgumentException("dest must not be null"); 391 } 392 synchronized (mLock) { 393 dest.writeString(mName); 394 nativeWriteToParcel(mNativeObject, dest); 395 } 396 if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) { 397 release(); 398 } 399 } 400 401 @Override toString()402 public String toString() { 403 synchronized (mLock) { 404 return "Surface(name=" + mName + ")/@0x" + 405 Integer.toHexString(System.identityHashCode(this)); 406 } 407 } 408 setNativeObjectLocked(long ptr)409 private void setNativeObjectLocked(long ptr) { 410 if (mNativeObject != ptr) { 411 if (mNativeObject == 0 && ptr != 0) { 412 mCloseGuard.open("release"); 413 } else if (mNativeObject != 0 && ptr == 0) { 414 mCloseGuard.close(); 415 } 416 mNativeObject = ptr; 417 mGenerationId += 1; 418 } 419 } 420 checkNotReleasedLocked()421 private void checkNotReleasedLocked() { 422 if (mNativeObject == 0) { 423 throw new IllegalStateException("Surface has already been released."); 424 } 425 } 426 427 /** 428 * Allocate buffers ahead of time to avoid allocation delays during rendering 429 * @hide 430 */ allocateBuffers()431 public void allocateBuffers() { 432 synchronized (mLock) { 433 checkNotReleasedLocked(); 434 nativeAllocateBuffers(mNativeObject); 435 } 436 } 437 438 /** 439 * Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or 440 * when a SurfaceTexture could not successfully be allocated. 441 */ 442 @SuppressWarnings("serial") 443 public static class OutOfResourcesException extends RuntimeException { OutOfResourcesException()444 public OutOfResourcesException() { 445 } OutOfResourcesException(String name)446 public OutOfResourcesException(String name) { 447 super(name); 448 } 449 } 450 451 /** 452 * Returns a human readable representation of a rotation. 453 * 454 * @param rotation The rotation. 455 * @return The rotation symbolic name. 456 * 457 * @hide 458 */ rotationToString(int rotation)459 public static String rotationToString(int rotation) { 460 switch (rotation) { 461 case Surface.ROTATION_0: { 462 return "ROTATION_0"; 463 } 464 case Surface.ROTATION_90: { 465 return "ROATATION_90"; 466 } 467 case Surface.ROTATION_180: { 468 return "ROATATION_180"; 469 } 470 case Surface.ROTATION_270: { 471 return "ROATATION_270"; 472 } 473 default: { 474 throw new IllegalArgumentException("Invalid rotation: " + rotation); 475 } 476 } 477 } 478 479 /** 480 * A Canvas class that can handle the compatibility mode. 481 * This does two things differently. 482 * <ul> 483 * <li>Returns the width and height of the target metrics, rather than 484 * native. For example, the canvas returns 320x480 even if an app is running 485 * in WVGA high density. 486 * <li>Scales the matrix in setMatrix by the application scale, except if 487 * the matrix looks like obtained from getMatrix. This is a hack to handle 488 * the case that an application uses getMatrix to keep the original matrix, 489 * set matrix of its own, then set the original matrix back. There is no 490 * perfect solution that works for all cases, and there are a lot of cases 491 * that this model does not work, but we hope this works for many apps. 492 * </ul> 493 */ 494 private final class CompatibleCanvas extends Canvas { 495 // A temp matrix to remember what an application obtained via {@link getMatrix} 496 private Matrix mOrigMatrix = null; 497 498 @Override setMatrix(Matrix matrix)499 public void setMatrix(Matrix matrix) { 500 if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) { 501 // don't scale the matrix if it's not compatibility mode, or 502 // the matrix was obtained from getMatrix. 503 super.setMatrix(matrix); 504 } else { 505 Matrix m = new Matrix(mCompatibleMatrix); 506 m.preConcat(matrix); 507 super.setMatrix(m); 508 } 509 } 510 511 @SuppressWarnings("deprecation") 512 @Override getMatrix(Matrix m)513 public void getMatrix(Matrix m) { 514 super.getMatrix(m); 515 if (mOrigMatrix == null) { 516 mOrigMatrix = new Matrix(); 517 } 518 mOrigMatrix.set(m); 519 } 520 } 521 } 522