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. 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     */
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
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     */
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     */
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
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
416    public int describeContents() {
417        return 0;
418    }
419
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
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
450    public String toString() {
451        synchronized (mLock) {
452            return "Surface(name=" + mName + ")/@0x" +
453                    Integer.toHexString(System.identityHashCode(this));
454        }
455    }
456
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
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     */
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 {
495        public OutOfResourcesException() {
496        }
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     */
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
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
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
578        HwuiContext() {
579            mRenderNode = RenderNode.create("HwuiCanvas", null);
580            mRenderNode.setClipToBounds(false);
581            mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject);
582        }
583
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
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
602        void updateSurface() {
603            nHwuiSetSurface(mHwuiRenderer, mNativeObject);
604        }
605
606        void destroy() {
607            if (mHwuiRenderer != 0) {
608                nHwuiDestroy(mHwuiRenderer);
609                mHwuiRenderer = 0;
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