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