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      * @hide
326      */
lockHardwareCanvas()327     public Canvas lockHardwareCanvas() {
328         synchronized (mLock) {
329             checkNotReleasedLocked();
330             if (mHwuiContext == null) {
331                 mHwuiContext = new HwuiContext();
332             }
333             return mHwuiContext.lockCanvas(
334                     nativeGetWidth(mNativeObject),
335                     nativeGetHeight(mNativeObject));
336         }
337     }
338 
339     /**
340      * @deprecated This API has been removed and is not supported.  Do not use.
341      */
342     @Deprecated
unlockCanvas(Canvas canvas)343     public void unlockCanvas(Canvas canvas) {
344         throw new UnsupportedOperationException();
345     }
346 
347     /**
348      * Sets the translator used to scale canvas's width/height in compatibility
349      * mode.
350      */
setCompatibilityTranslator(Translator translator)351     void setCompatibilityTranslator(Translator translator) {
352         if (translator != null) {
353             float appScale = translator.applicationScale;
354             mCompatibleMatrix = new Matrix();
355             mCompatibleMatrix.setScale(appScale, appScale);
356         }
357     }
358 
359     /**
360      * Copy another surface to this one.  This surface now holds a reference
361      * to the same data as the original surface, and is -not- the owner.
362      * This is for use by the window manager when returning a window surface
363      * back from a client, converting it from the representation being managed
364      * by the window manager to the representation the client uses to draw
365      * in to it.
366      * @hide
367      */
copyFrom(SurfaceControl other)368     public void copyFrom(SurfaceControl other) {
369         if (other == null) {
370             throw new IllegalArgumentException("other must not be null");
371         }
372 
373         long surfaceControlPtr = other.mNativeObject;
374         if (surfaceControlPtr == 0) {
375             throw new NullPointerException(
376                     "SurfaceControl native object is null. Are you using a released SurfaceControl?");
377         }
378         long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
379 
380         synchronized (mLock) {
381             if (mNativeObject != 0) {
382                 nativeRelease(mNativeObject);
383             }
384             setNativeObjectLocked(newNativeObject);
385         }
386     }
387 
388     /**
389      * This is intended to be used by {@link SurfaceView#updateWindow} only.
390      * @param other access is not thread safe
391      * @hide
392      * @deprecated
393      */
394     @Deprecated
transferFrom(Surface other)395     public void transferFrom(Surface other) {
396         if (other == null) {
397             throw new IllegalArgumentException("other must not be null");
398         }
399         if (other != this) {
400             final long newPtr;
401             synchronized (other.mLock) {
402                 newPtr = other.mNativeObject;
403                 other.setNativeObjectLocked(0);
404             }
405 
406             synchronized (mLock) {
407                 if (mNativeObject != 0) {
408                     nativeRelease(mNativeObject);
409                 }
410                 setNativeObjectLocked(newPtr);
411             }
412         }
413     }
414 
415     @Override
describeContents()416     public int describeContents() {
417         return 0;
418     }
419 
readFromParcel(Parcel source)420     public void readFromParcel(Parcel source) {
421         if (source == null) {
422             throw new IllegalArgumentException("source must not be null");
423         }
424 
425         synchronized (mLock) {
426             // nativeReadFromParcel() will either return mNativeObject, or
427             // create a new native Surface and return it after reducing
428             // the reference count on mNativeObject.  Either way, it is
429             // not necessary to call nativeRelease() here.
430             mName = source.readString();
431             setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
432         }
433     }
434 
435     @Override
writeToParcel(Parcel dest, int flags)436     public void writeToParcel(Parcel dest, int flags) {
437         if (dest == null) {
438             throw new IllegalArgumentException("dest must not be null");
439         }
440         synchronized (mLock) {
441             dest.writeString(mName);
442             nativeWriteToParcel(mNativeObject, dest);
443         }
444         if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
445             release();
446         }
447     }
448 
449     @Override
toString()450     public String toString() {
451         synchronized (mLock) {
452             return "Surface(name=" + mName + ")/@0x" +
453                     Integer.toHexString(System.identityHashCode(this));
454         }
455     }
456 
setNativeObjectLocked(long ptr)457     private void setNativeObjectLocked(long ptr) {
458         if (mNativeObject != ptr) {
459             if (mNativeObject == 0 && ptr != 0) {
460                 mCloseGuard.open("release");
461             } else if (mNativeObject != 0 && ptr == 0) {
462                 mCloseGuard.close();
463             }
464             mNativeObject = ptr;
465             mGenerationId += 1;
466             if (mHwuiContext != null) {
467                 mHwuiContext.updateSurface();
468             }
469         }
470     }
471 
checkNotReleasedLocked()472     private void checkNotReleasedLocked() {
473         if (mNativeObject == 0) {
474             throw new IllegalStateException("Surface has already been released.");
475         }
476     }
477 
478     /**
479      * Allocate buffers ahead of time to avoid allocation delays during rendering
480      * @hide
481      */
allocateBuffers()482     public void allocateBuffers() {
483         synchronized (mLock) {
484             checkNotReleasedLocked();
485             nativeAllocateBuffers(mNativeObject);
486         }
487     }
488 
489     /**
490      * Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or
491      * when a SurfaceTexture could not successfully be allocated.
492      */
493     @SuppressWarnings("serial")
494     public static class OutOfResourcesException extends RuntimeException {
OutOfResourcesException()495         public OutOfResourcesException() {
496         }
OutOfResourcesException(String name)497         public OutOfResourcesException(String name) {
498             super(name);
499         }
500     }
501 
502     /**
503      * Returns a human readable representation of a rotation.
504      *
505      * @param rotation The rotation.
506      * @return The rotation symbolic name.
507      *
508      * @hide
509      */
rotationToString(int rotation)510     public static String rotationToString(int rotation) {
511         switch (rotation) {
512             case Surface.ROTATION_0: {
513                 return "ROTATION_0";
514             }
515             case Surface.ROTATION_90: {
516                 return "ROATATION_90";
517             }
518             case Surface.ROTATION_180: {
519                 return "ROATATION_180";
520             }
521             case Surface.ROTATION_270: {
522                 return "ROATATION_270";
523             }
524             default: {
525                 throw new IllegalArgumentException("Invalid rotation: " + rotation);
526             }
527         }
528     }
529 
530     /**
531      * A Canvas class that can handle the compatibility mode.
532      * This does two things differently.
533      * <ul>
534      * <li>Returns the width and height of the target metrics, rather than
535      * native. For example, the canvas returns 320x480 even if an app is running
536      * in WVGA high density.
537      * <li>Scales the matrix in setMatrix by the application scale, except if
538      * the matrix looks like obtained from getMatrix. This is a hack to handle
539      * the case that an application uses getMatrix to keep the original matrix,
540      * set matrix of its own, then set the original matrix back. There is no
541      * perfect solution that works for all cases, and there are a lot of cases
542      * that this model does not work, but we hope this works for many apps.
543      * </ul>
544      */
545     private final class CompatibleCanvas extends Canvas {
546         // A temp matrix to remember what an application obtained via {@link getMatrix}
547         private Matrix mOrigMatrix = null;
548 
549         @Override
setMatrix(Matrix matrix)550         public void setMatrix(Matrix matrix) {
551             if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) {
552                 // don't scale the matrix if it's not compatibility mode, or
553                 // the matrix was obtained from getMatrix.
554                 super.setMatrix(matrix);
555             } else {
556                 Matrix m = new Matrix(mCompatibleMatrix);
557                 m.preConcat(matrix);
558                 super.setMatrix(m);
559             }
560         }
561 
562         @SuppressWarnings("deprecation")
563         @Override
getMatrix(Matrix m)564         public void getMatrix(Matrix m) {
565             super.getMatrix(m);
566             if (mOrigMatrix == null) {
567                 mOrigMatrix = new Matrix();
568             }
569             mOrigMatrix.set(m);
570         }
571     }
572 
573     private final class HwuiContext {
574         private final RenderNode mRenderNode;
575         private long mHwuiRenderer;
576         private HardwareCanvas mCanvas;
577 
HwuiContext()578         HwuiContext() {
579             mRenderNode = RenderNode.create("HwuiCanvas", null);
580             mRenderNode.setClipToBounds(false);
581             mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject);
582         }
583 
lockCanvas(int width, int height)584         Canvas lockCanvas(int width, int height) {
585             if (mCanvas != null) {
586                 throw new IllegalStateException("Surface was already locked!");
587             }
588             mCanvas = mRenderNode.start(width, height);
589             return mCanvas;
590         }
591 
unlockAndPost(Canvas canvas)592         void unlockAndPost(Canvas canvas) {
593             if (canvas != mCanvas) {
594                 throw new IllegalArgumentException("canvas object must be the same instance that "
595                         + "was previously returned by lockCanvas");
596             }
597             mRenderNode.end(mCanvas);
598             mCanvas = null;
599             nHwuiDraw(mHwuiRenderer);
600         }
601 
updateSurface()602         void updateSurface() {
603             nHwuiSetSurface(mHwuiRenderer, mNativeObject);
604         }
605 
destroy()606         void destroy() {
607             if (mHwuiRenderer != 0) {
608                 nHwuiDestroy(mHwuiRenderer);
609                 mHwuiRenderer = 0;
610             }
611         }
612     }
613 
nHwuiCreate(long rootNode, long surface)614     private static native long nHwuiCreate(long rootNode, long surface);
nHwuiSetSurface(long renderer, long surface)615     private static native void nHwuiSetSurface(long renderer, long surface);
nHwuiDraw(long renderer)616     private static native void nHwuiDraw(long renderer);
nHwuiDestroy(long renderer)617     private static native void nHwuiDestroy(long renderer);
618 }
619