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