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