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