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