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 
nativeGetWidth(long nativeObject)56     private static native int nativeGetWidth(long nativeObject);
nativeGetHeight(long nativeObject)57     private static native int nativeGetHeight(long nativeObject);
58 
nativeGetNextFrameNumber(long nativeObject)59     private static native long nativeGetNextFrameNumber(long nativeObject);
nativeSetScalingMode(long nativeObject, int scalingMode)60     private static native int nativeSetScalingMode(long nativeObject, int scalingMode);
61 
62     public static final Parcelable.Creator<Surface> CREATOR =
63             new Parcelable.Creator<Surface>() {
64         @Override
65         public Surface createFromParcel(Parcel source) {
66             try {
67                 Surface s = new Surface();
68                 s.readFromParcel(source);
69                 return s;
70             } catch (Exception e) {
71                 Log.e(TAG, "Exception creating surface from parcel", e);
72                 return null;
73             }
74         }
75 
76         @Override
77         public Surface[] newArray(int size) {
78             return new Surface[size];
79         }
80     };
81 
82     private final CloseGuard mCloseGuard = CloseGuard.get();
83 
84     // Guarded state.
85     final Object mLock = new Object(); // protects the native state
86     private String mName;
87     long mNativeObject; // package scope only for SurfaceControl access
88     private long mLockedObject;
89     private int mGenerationId; // incremented each time mNativeObject changes
90     private final Canvas mCanvas = new CompatibleCanvas();
91 
92     // A matrix to scale the matrix set by application. This is set to null for
93     // non compatibility mode.
94     private Matrix mCompatibleMatrix;
95 
96     private HwuiContext mHwuiContext;
97 
98     /** @hide */
99     @Retention(RetentionPolicy.SOURCE)
100     @IntDef({SCALING_MODE_FREEZE, SCALING_MODE_SCALE_TO_WINDOW,
101                     SCALING_MODE_SCALE_CROP, SCALING_MODE_NO_SCALE_CROP})
102     public @interface ScalingMode {}
103     // From system/window.h
104     /** @hide */
105     public static final int SCALING_MODE_FREEZE = 0;
106     /** @hide */
107     public static final int SCALING_MODE_SCALE_TO_WINDOW = 1;
108     /** @hide */
109     public static final int SCALING_MODE_SCALE_CROP = 2;
110     /** @hide */
111     public static final int SCALING_MODE_NO_SCALE_CROP = 3;
112 
113     /** @hide */
114     @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
115     @Retention(RetentionPolicy.SOURCE)
116     public @interface Rotation {}
117 
118     /**
119      * Rotation constant: 0 degree rotation (natural orientation)
120      */
121     public static final int ROTATION_0 = 0;
122 
123     /**
124      * Rotation constant: 90 degree rotation.
125      */
126     public static final int ROTATION_90 = 1;
127 
128     /**
129      * Rotation constant: 180 degree rotation.
130      */
131     public static final int ROTATION_180 = 2;
132 
133     /**
134      * Rotation constant: 270 degree rotation.
135      */
136     public static final int ROTATION_270 = 3;
137 
138     /**
139      * Create an empty surface, which will later be filled in by readFromParcel().
140      * @hide
141      */
Surface()142     public Surface() {
143     }
144 
145     /**
146      * Create Surface from a {@link SurfaceTexture}.
147      *
148      * Images drawn to the Surface will be made available to the {@link
149      * SurfaceTexture}, which can attach them to an OpenGL ES texture via {@link
150      * SurfaceTexture#updateTexImage}.
151      *
152      * @param surfaceTexture The {@link SurfaceTexture} that is updated by this
153      * Surface.
154      * @throws OutOfResourcesException if the surface could not be created.
155      */
Surface(SurfaceTexture surfaceTexture)156     public Surface(SurfaceTexture surfaceTexture) {
157         if (surfaceTexture == null) {
158             throw new IllegalArgumentException("surfaceTexture must not be null");
159         }
160 
161         synchronized (mLock) {
162             mName = surfaceTexture.toString();
163             setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
164         }
165     }
166 
167     /* called from android_view_Surface_createFromIGraphicBufferProducer() */
Surface(long nativeObject)168     private Surface(long nativeObject) {
169         synchronized (mLock) {
170             setNativeObjectLocked(nativeObject);
171         }
172     }
173 
174     @Override
finalize()175     protected void finalize() throws Throwable {
176         try {
177             if (mCloseGuard != null) {
178                 mCloseGuard.warnIfOpen();
179             }
180             release();
181         } finally {
182             super.finalize();
183         }
184     }
185 
186     /**
187      * Release the local reference to the server-side surface.
188      * Always call release() when you're done with a Surface.
189      * This will make the surface invalid.
190      */
release()191     public void release() {
192         synchronized (mLock) {
193             if (mNativeObject != 0) {
194                 nativeRelease(mNativeObject);
195                 setNativeObjectLocked(0);
196             }
197             if (mHwuiContext != null) {
198                 mHwuiContext.destroy();
199                 mHwuiContext = null;
200             }
201         }
202     }
203 
204     /**
205      * Free all server-side state associated with this surface and
206      * release this object's reference.  This method can only be
207      * called from the process that created the service.
208      * @hide
209      */
destroy()210     public void destroy() {
211         release();
212     }
213 
214     /**
215      * Returns true if this object holds a valid surface.
216      *
217      * @return True if it holds a physical surface, so lockCanvas() will succeed.
218      * Otherwise returns false.
219      */
isValid()220     public boolean isValid() {
221         synchronized (mLock) {
222             if (mNativeObject == 0) return false;
223             return nativeIsValid(mNativeObject);
224         }
225     }
226 
227     /**
228      * Gets the generation number of this surface, incremented each time
229      * the native surface contained within this object changes.
230      *
231      * @return The current generation number.
232      * @hide
233      */
getGenerationId()234     public int getGenerationId() {
235         synchronized (mLock) {
236             return mGenerationId;
237         }
238     }
239 
240     /**
241      * Returns the next frame number which will be dequeued for rendering.
242      * Intended for use with SurfaceFlinger's deferred transactions API.
243      *
244      * @hide
245      */
getNextFrameNumber()246     public long getNextFrameNumber() {
247         synchronized (mLock) {
248             return nativeGetNextFrameNumber(mNativeObject);
249         }
250     }
251 
252     /**
253      * Returns true if the consumer of this Surface is running behind the producer.
254      *
255      * @return True if the consumer is more than one buffer ahead of the producer.
256      * @hide
257      */
isConsumerRunningBehind()258     public boolean isConsumerRunningBehind() {
259         synchronized (mLock) {
260             checkNotReleasedLocked();
261             return nativeIsConsumerRunningBehind(mNativeObject);
262         }
263     }
264 
265     /**
266      * Gets a {@link Canvas} for drawing into this surface.
267      *
268      * After drawing into the provided {@link Canvas}, the caller must
269      * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
270      *
271      * @param inOutDirty A rectangle that represents the dirty region that the caller wants
272      * to redraw.  This function may choose to expand the dirty rectangle if for example
273      * the surface has been resized or if the previous contents of the surface were
274      * not available.  The caller must redraw the entire dirty region as represented
275      * by the contents of the inOutDirty rectangle upon return from this function.
276      * The caller may also pass <code>null</code> instead, in the case where the
277      * entire surface should be redrawn.
278      * @return A canvas for drawing into the surface.
279      *
280      * @throws IllegalArgumentException If the inOutDirty rectangle is not valid.
281      * @throws OutOfResourcesException If the canvas cannot be locked.
282      */
lockCanvas(Rect inOutDirty)283     public Canvas lockCanvas(Rect inOutDirty)
284             throws Surface.OutOfResourcesException, IllegalArgumentException {
285         synchronized (mLock) {
286             checkNotReleasedLocked();
287             if (mLockedObject != 0) {
288                 // Ideally, nativeLockCanvas() would throw in this situation and prevent the
289                 // double-lock, but that won't happen if mNativeObject was updated.  We can't
290                 // abandon the old mLockedObject because it might still be in use, so instead
291                 // we just refuse to re-lock the Surface.
292                 throw new IllegalArgumentException("Surface was already locked");
293             }
294             mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
295             return mCanvas;
296         }
297     }
298 
299     /**
300      * Posts the new contents of the {@link Canvas} to the surface and
301      * releases the {@link Canvas}.
302      *
303      * @param canvas The canvas previously obtained from {@link #lockCanvas}.
304      */
unlockCanvasAndPost(Canvas canvas)305     public void unlockCanvasAndPost(Canvas canvas) {
306         synchronized (mLock) {
307             checkNotReleasedLocked();
308 
309             if (mHwuiContext != null) {
310                 mHwuiContext.unlockAndPost(canvas);
311             } else {
312                 unlockSwCanvasAndPost(canvas);
313             }
314         }
315     }
316 
unlockSwCanvasAndPost(Canvas canvas)317     private void unlockSwCanvasAndPost(Canvas canvas) {
318         if (canvas != mCanvas) {
319             throw new IllegalArgumentException("canvas object must be the same instance that "
320                     + "was previously returned by lockCanvas");
321         }
322         if (mNativeObject != mLockedObject) {
323             Log.w(TAG, "WARNING: Surface's mNativeObject (0x" +
324                     Long.toHexString(mNativeObject) + ") != mLockedObject (0x" +
325                     Long.toHexString(mLockedObject) +")");
326         }
327         if (mLockedObject == 0) {
328             throw new IllegalStateException("Surface was not locked");
329         }
330         try {
331             nativeUnlockCanvasAndPost(mLockedObject, canvas);
332         } finally {
333             nativeRelease(mLockedObject);
334             mLockedObject = 0;
335         }
336     }
337 
338     /**
339      * Gets a {@link Canvas} for drawing into this surface.
340      *
341      * After drawing into the provided {@link Canvas}, the caller must
342      * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
343      *
344      * Unlike {@link #lockCanvas(Rect)} this will return a hardware-accelerated
345      * canvas. See the <a href="{@docRoot}guide/topics/graphics/hardware-accel.html#unsupported">
346      * unsupported drawing operations</a> for a list of what is and isn't
347      * supported in a hardware-accelerated canvas. It is also required to
348      * fully cover the surface every time {@link #lockHardwareCanvas()} is
349      * called as the buffer is not preserved between frames. Partial updates
350      * are not supported.
351      *
352      * @return A canvas for drawing into the surface.
353      *
354      * @throws IllegalStateException If the canvas cannot be locked.
355      */
lockHardwareCanvas()356     public Canvas lockHardwareCanvas() {
357         synchronized (mLock) {
358             checkNotReleasedLocked();
359             if (mHwuiContext == null) {
360                 mHwuiContext = new HwuiContext();
361             }
362             return mHwuiContext.lockCanvas(
363                     nativeGetWidth(mNativeObject),
364                     nativeGetHeight(mNativeObject));
365         }
366     }
367 
368     /**
369      * @deprecated This API has been removed and is not supported.  Do not use.
370      */
371     @Deprecated
unlockCanvas(Canvas canvas)372     public void unlockCanvas(Canvas canvas) {
373         throw new UnsupportedOperationException();
374     }
375 
376     /**
377      * Sets the translator used to scale canvas's width/height in compatibility
378      * mode.
379      */
setCompatibilityTranslator(Translator translator)380     void setCompatibilityTranslator(Translator translator) {
381         if (translator != null) {
382             float appScale = translator.applicationScale;
383             mCompatibleMatrix = new Matrix();
384             mCompatibleMatrix.setScale(appScale, appScale);
385         }
386     }
387 
388     /**
389      * Copy another surface to this one.  This surface now holds a reference
390      * to the same data as the original surface, and is -not- the owner.
391      * This is for use by the window manager when returning a window surface
392      * back from a client, converting it from the representation being managed
393      * by the window manager to the representation the client uses to draw
394      * in to it.
395      * @hide
396      */
copyFrom(SurfaceControl other)397     public void copyFrom(SurfaceControl other) {
398         if (other == null) {
399             throw new IllegalArgumentException("other must not be null");
400         }
401 
402         long surfaceControlPtr = other.mNativeObject;
403         if (surfaceControlPtr == 0) {
404             throw new NullPointerException(
405                     "SurfaceControl native object is null. Are you using a released SurfaceControl?");
406         }
407         long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
408 
409         synchronized (mLock) {
410             if (mNativeObject != 0) {
411                 nativeRelease(mNativeObject);
412             }
413             setNativeObjectLocked(newNativeObject);
414         }
415     }
416 
417     /**
418      * This is intended to be used by {@link SurfaceView#updateWindow} only.
419      * @param other access is not thread safe
420      * @hide
421      * @deprecated
422      */
423     @Deprecated
transferFrom(Surface other)424     public void transferFrom(Surface other) {
425         if (other == null) {
426             throw new IllegalArgumentException("other must not be null");
427         }
428         if (other != this) {
429             final long newPtr;
430             synchronized (other.mLock) {
431                 newPtr = other.mNativeObject;
432                 other.setNativeObjectLocked(0);
433             }
434 
435             synchronized (mLock) {
436                 if (mNativeObject != 0) {
437                     nativeRelease(mNativeObject);
438                 }
439                 setNativeObjectLocked(newPtr);
440             }
441         }
442     }
443 
444     @Override
describeContents()445     public int describeContents() {
446         return 0;
447     }
448 
readFromParcel(Parcel source)449     public void readFromParcel(Parcel source) {
450         if (source == null) {
451             throw new IllegalArgumentException("source must not be null");
452         }
453 
454         synchronized (mLock) {
455             // nativeReadFromParcel() will either return mNativeObject, or
456             // create a new native Surface and return it after reducing
457             // the reference count on mNativeObject.  Either way, it is
458             // not necessary to call nativeRelease() here.
459             mName = source.readString();
460             setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
461         }
462     }
463 
464     @Override
writeToParcel(Parcel dest, int flags)465     public void writeToParcel(Parcel dest, int flags) {
466         if (dest == null) {
467             throw new IllegalArgumentException("dest must not be null");
468         }
469         synchronized (mLock) {
470             dest.writeString(mName);
471             nativeWriteToParcel(mNativeObject, dest);
472         }
473         if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
474             release();
475         }
476     }
477 
478     @Override
toString()479     public String toString() {
480         synchronized (mLock) {
481             return "Surface(name=" + mName + ")/@0x" +
482                     Integer.toHexString(System.identityHashCode(this));
483         }
484     }
485 
setNativeObjectLocked(long ptr)486     private void setNativeObjectLocked(long ptr) {
487         if (mNativeObject != ptr) {
488             if (mNativeObject == 0 && ptr != 0) {
489                 mCloseGuard.open("release");
490             } else if (mNativeObject != 0 && ptr == 0) {
491                 mCloseGuard.close();
492             }
493             mNativeObject = ptr;
494             mGenerationId += 1;
495             if (mHwuiContext != null) {
496                 mHwuiContext.updateSurface();
497             }
498         }
499     }
500 
checkNotReleasedLocked()501     private void checkNotReleasedLocked() {
502         if (mNativeObject == 0) {
503             throw new IllegalStateException("Surface has already been released.");
504         }
505     }
506 
507     /**
508      * Allocate buffers ahead of time to avoid allocation delays during rendering
509      * @hide
510      */
allocateBuffers()511     public void allocateBuffers() {
512         synchronized (mLock) {
513             checkNotReleasedLocked();
514             nativeAllocateBuffers(mNativeObject);
515         }
516     }
517 
518     /**
519      * Set the scaling mode to be used for this surfaces buffers
520      * @hide
521      */
setScalingMode(@calingMode int scalingMode)522     void setScalingMode(@ScalingMode int scalingMode) {
523         synchronized (mLock) {
524             checkNotReleasedLocked();
525             int err = nativeSetScalingMode(mNativeObject, scalingMode);
526             if (err != 0) {
527                 throw new IllegalArgumentException("Invalid scaling mode: " + scalingMode);
528             }
529         }
530     }
531 
532     /**
533      * Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or
534      * when a SurfaceTexture could not successfully be allocated.
535      */
536     @SuppressWarnings("serial")
537     public static class OutOfResourcesException extends RuntimeException {
OutOfResourcesException()538         public OutOfResourcesException() {
539         }
OutOfResourcesException(String name)540         public OutOfResourcesException(String name) {
541             super(name);
542         }
543     }
544 
545     /**
546      * Returns a human readable representation of a rotation.
547      *
548      * @param rotation The rotation.
549      * @return The rotation symbolic name.
550      *
551      * @hide
552      */
rotationToString(int rotation)553     public static String rotationToString(int rotation) {
554         switch (rotation) {
555             case Surface.ROTATION_0: {
556                 return "ROTATION_0";
557             }
558             case Surface.ROTATION_90: {
559                 return "ROATATION_90";
560             }
561             case Surface.ROTATION_180: {
562                 return "ROATATION_180";
563             }
564             case Surface.ROTATION_270: {
565                 return "ROATATION_270";
566             }
567             default: {
568                 throw new IllegalArgumentException("Invalid rotation: " + rotation);
569             }
570         }
571     }
572 
573     /**
574      * A Canvas class that can handle the compatibility mode.
575      * This does two things differently.
576      * <ul>
577      * <li>Returns the width and height of the target metrics, rather than
578      * native. For example, the canvas returns 320x480 even if an app is running
579      * in WVGA high density.
580      * <li>Scales the matrix in setMatrix by the application scale, except if
581      * the matrix looks like obtained from getMatrix. This is a hack to handle
582      * the case that an application uses getMatrix to keep the original matrix,
583      * set matrix of its own, then set the original matrix back. There is no
584      * perfect solution that works for all cases, and there are a lot of cases
585      * that this model does not work, but we hope this works for many apps.
586      * </ul>
587      */
588     private final class CompatibleCanvas extends Canvas {
589         // A temp matrix to remember what an application obtained via {@link getMatrix}
590         private Matrix mOrigMatrix = null;
591 
592         @Override
setMatrix(Matrix matrix)593         public void setMatrix(Matrix matrix) {
594             if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) {
595                 // don't scale the matrix if it's not compatibility mode, or
596                 // the matrix was obtained from getMatrix.
597                 super.setMatrix(matrix);
598             } else {
599                 Matrix m = new Matrix(mCompatibleMatrix);
600                 m.preConcat(matrix);
601                 super.setMatrix(m);
602             }
603         }
604 
605         @SuppressWarnings("deprecation")
606         @Override
getMatrix(Matrix m)607         public void getMatrix(Matrix m) {
608             super.getMatrix(m);
609             if (mOrigMatrix == null) {
610                 mOrigMatrix = new Matrix();
611             }
612             mOrigMatrix.set(m);
613         }
614     }
615 
616     private final class HwuiContext {
617         private final RenderNode mRenderNode;
618         private long mHwuiRenderer;
619         private DisplayListCanvas mCanvas;
620 
HwuiContext()621         HwuiContext() {
622             mRenderNode = RenderNode.create("HwuiCanvas", null);
623             mRenderNode.setClipToBounds(false);
624             mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject);
625         }
626 
lockCanvas(int width, int height)627         Canvas lockCanvas(int width, int height) {
628             if (mCanvas != null) {
629                 throw new IllegalStateException("Surface was already locked!");
630             }
631             mCanvas = mRenderNode.start(width, height);
632             return mCanvas;
633         }
634 
unlockAndPost(Canvas canvas)635         void unlockAndPost(Canvas canvas) {
636             if (canvas != mCanvas) {
637                 throw new IllegalArgumentException("canvas object must be the same instance that "
638                         + "was previously returned by lockCanvas");
639             }
640             mRenderNode.end(mCanvas);
641             mCanvas = null;
642             nHwuiDraw(mHwuiRenderer);
643         }
644 
updateSurface()645         void updateSurface() {
646             nHwuiSetSurface(mHwuiRenderer, mNativeObject);
647         }
648 
destroy()649         void destroy() {
650             if (mHwuiRenderer != 0) {
651                 nHwuiDestroy(mHwuiRenderer);
652                 mHwuiRenderer = 0;
653             }
654         }
655     }
656 
nHwuiCreate(long rootNode, long surface)657     private static native long nHwuiCreate(long rootNode, long surface);
nHwuiSetSurface(long renderer, long surface)658     private static native void nHwuiSetSurface(long renderer, long surface);
nHwuiDraw(long renderer)659     private static native void nHwuiDraw(long renderer);
nHwuiDestroy(long renderer)660     private static native void nHwuiDestroy(long renderer);
661 }
662