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