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