• 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.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