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