• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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