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 *
37 * <p>A Surface is generally created by or from a consumer of image buffers (such as a
38 * {@link android.graphics.SurfaceTexture}, {@link android.media.MediaRecorder}, or
39 * {@link android.renderscript.Allocation}), and is handed to some kind of producer (such as
40 * {@link android.opengl.EGL14#eglCreateWindowSurface(android.opengl.EGLDisplay,android.opengl.EGLConfig,java.lang.Object,int[],int) OpenGL},
41 * {@link android.media.MediaPlayer#setSurface MediaPlayer}, or
42 * {@link android.hardware.camera2.CameraDevice#createCaptureSession CameraDevice}) to draw
43 * into.</p>
44 *
45 * <p><strong>Note:</strong> A Surface acts like a
46 * {@link java.lang.ref.WeakReference weak reference} to the consumer it is associated with. By
47 * itself it will not keep its parent consumer from being reclaimed.</p>
48 */
49public class Surface implements Parcelable {
50    private static final String TAG = "Surface";
51
52    private static native long nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
53            throws OutOfResourcesException;
54    private static native long nativeCreateFromSurfaceControl(long surfaceControlNativeObject);
55
56    private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)
57            throws OutOfResourcesException;
58    private static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas);
59
60    private static native void nativeRelease(long nativeObject);
61    private static native boolean nativeIsValid(long nativeObject);
62    private static native boolean nativeIsConsumerRunningBehind(long nativeObject);
63    private static native long nativeReadFromParcel(long nativeObject, Parcel source);
64    private static native void nativeWriteToParcel(long nativeObject, Parcel dest);
65
66    private static native void nativeAllocateBuffers(long nativeObject);
67
68    private static native int nativeGetWidth(long nativeObject);
69    private static native int nativeGetHeight(long nativeObject);
70
71    private static native long nativeGetNextFrameNumber(long nativeObject);
72    private static native int nativeSetScalingMode(long nativeObject, int scalingMode);
73    private static native void nativeSetBuffersTransform(long nativeObject, long transform);
74    private static native int nativeForceScopedDisconnect(long nativeObject);
75
76    public static final Parcelable.Creator<Surface> CREATOR =
77            new Parcelable.Creator<Surface>() {
78        @Override
79        public Surface createFromParcel(Parcel source) {
80            try {
81                Surface s = new Surface();
82                s.readFromParcel(source);
83                return s;
84            } catch (Exception e) {
85                Log.e(TAG, "Exception creating surface from parcel", e);
86                return null;
87            }
88        }
89
90        @Override
91        public Surface[] newArray(int size) {
92            return new Surface[size];
93        }
94    };
95
96    private final CloseGuard mCloseGuard = CloseGuard.get();
97
98    // Guarded state.
99    final Object mLock = new Object(); // protects the native state
100    private String mName;
101    long mNativeObject; // package scope only for SurfaceControl access
102    private long mLockedObject;
103    private int mGenerationId; // incremented each time mNativeObject changes
104    private final Canvas mCanvas = new CompatibleCanvas();
105
106    // A matrix to scale the matrix set by application. This is set to null for
107    // non compatibility mode.
108    private Matrix mCompatibleMatrix;
109
110    private HwuiContext mHwuiContext;
111
112    private boolean mIsSingleBuffered;
113
114    /** @hide */
115    @Retention(RetentionPolicy.SOURCE)
116    @IntDef({SCALING_MODE_FREEZE, SCALING_MODE_SCALE_TO_WINDOW,
117                    SCALING_MODE_SCALE_CROP, SCALING_MODE_NO_SCALE_CROP})
118    public @interface ScalingMode {}
119    // From system/window.h
120    /** @hide */
121    public static final int SCALING_MODE_FREEZE = 0;
122    /** @hide */
123    public static final int SCALING_MODE_SCALE_TO_WINDOW = 1;
124    /** @hide */
125    public static final int SCALING_MODE_SCALE_CROP = 2;
126    /** @hide */
127    public static final int SCALING_MODE_NO_SCALE_CROP = 3;
128
129    /** @hide */
130    @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
131    @Retention(RetentionPolicy.SOURCE)
132    public @interface Rotation {}
133
134    /**
135     * Rotation constant: 0 degree rotation (natural orientation)
136     */
137    public static final int ROTATION_0 = 0;
138
139    /**
140     * Rotation constant: 90 degree rotation.
141     */
142    public static final int ROTATION_90 = 1;
143
144    /**
145     * Rotation constant: 180 degree rotation.
146     */
147    public static final int ROTATION_180 = 2;
148
149    /**
150     * Rotation constant: 270 degree rotation.
151     */
152    public static final int ROTATION_270 = 3;
153
154    /**
155     * Create an empty surface, which will later be filled in by readFromParcel().
156     * @hide
157     */
158    public Surface() {
159    }
160
161    /**
162     * Create Surface from a {@link SurfaceTexture}.
163     *
164     * Images drawn to the Surface will be made available to the {@link
165     * SurfaceTexture}, which can attach them to an OpenGL ES texture via {@link
166     * SurfaceTexture#updateTexImage}.
167     *
168     * @param surfaceTexture The {@link SurfaceTexture} that is updated by this
169     * Surface.
170     * @throws OutOfResourcesException if the surface could not be created.
171     */
172    public Surface(SurfaceTexture surfaceTexture) {
173        if (surfaceTexture == null) {
174            throw new IllegalArgumentException("surfaceTexture must not be null");
175        }
176        mIsSingleBuffered = surfaceTexture.isSingleBuffered();
177        synchronized (mLock) {
178            mName = surfaceTexture.toString();
179            setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
180        }
181    }
182
183    /* called from android_view_Surface_createFromIGraphicBufferProducer() */
184    private Surface(long nativeObject) {
185        synchronized (mLock) {
186            setNativeObjectLocked(nativeObject);
187        }
188    }
189
190    @Override
191    protected void finalize() throws Throwable {
192        try {
193            if (mCloseGuard != null) {
194                mCloseGuard.warnIfOpen();
195            }
196            release();
197        } finally {
198            super.finalize();
199        }
200    }
201
202    /**
203     * Release the local reference to the server-side surface.
204     * Always call release() when you're done with a Surface.
205     * This will make the surface invalid.
206     */
207    public void release() {
208        synchronized (mLock) {
209            if (mNativeObject != 0) {
210                nativeRelease(mNativeObject);
211                setNativeObjectLocked(0);
212            }
213            if (mHwuiContext != null) {
214                mHwuiContext.destroy();
215                mHwuiContext = null;
216            }
217        }
218    }
219
220    /**
221     * Free all server-side state associated with this surface and
222     * release this object's reference.  This method can only be
223     * called from the process that created the service.
224     * @hide
225     */
226    public void destroy() {
227        release();
228    }
229
230    /**
231     * Returns true if this object holds a valid surface.
232     *
233     * @return True if it holds a physical surface, so lockCanvas() will succeed.
234     * Otherwise returns false.
235     */
236    public boolean isValid() {
237        synchronized (mLock) {
238            if (mNativeObject == 0) return false;
239            return nativeIsValid(mNativeObject);
240        }
241    }
242
243    /**
244     * Gets the generation number of this surface, incremented each time
245     * the native surface contained within this object changes.
246     *
247     * @return The current generation number.
248     * @hide
249     */
250    public int getGenerationId() {
251        synchronized (mLock) {
252            return mGenerationId;
253        }
254    }
255
256    /**
257     * Returns the next frame number which will be dequeued for rendering.
258     * Intended for use with SurfaceFlinger's deferred transactions API.
259     *
260     * @hide
261     */
262    public long getNextFrameNumber() {
263        synchronized (mLock) {
264            return nativeGetNextFrameNumber(mNativeObject);
265        }
266    }
267
268    /**
269     * Returns true if the consumer of this Surface is running behind the producer.
270     *
271     * @return True if the consumer is more than one buffer ahead of the producer.
272     * @hide
273     */
274    public boolean isConsumerRunningBehind() {
275        synchronized (mLock) {
276            checkNotReleasedLocked();
277            return nativeIsConsumerRunningBehind(mNativeObject);
278        }
279    }
280
281    /**
282     * Gets a {@link Canvas} for drawing into this surface.
283     *
284     * After drawing into the provided {@link Canvas}, the caller must
285     * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
286     *
287     * @param inOutDirty A rectangle that represents the dirty region that the caller wants
288     * to redraw.  This function may choose to expand the dirty rectangle if for example
289     * the surface has been resized or if the previous contents of the surface were
290     * not available.  The caller must redraw the entire dirty region as represented
291     * by the contents of the inOutDirty rectangle upon return from this function.
292     * The caller may also pass <code>null</code> instead, in the case where the
293     * entire surface should be redrawn.
294     * @return A canvas for drawing into the surface.
295     *
296     * @throws IllegalArgumentException If the inOutDirty rectangle is not valid.
297     * @throws OutOfResourcesException If the canvas cannot be locked.
298     */
299    public Canvas lockCanvas(Rect inOutDirty)
300            throws Surface.OutOfResourcesException, IllegalArgumentException {
301        synchronized (mLock) {
302            checkNotReleasedLocked();
303            if (mLockedObject != 0) {
304                // Ideally, nativeLockCanvas() would throw in this situation and prevent the
305                // double-lock, but that won't happen if mNativeObject was updated.  We can't
306                // abandon the old mLockedObject because it might still be in use, so instead
307                // we just refuse to re-lock the Surface.
308                throw new IllegalArgumentException("Surface was already locked");
309            }
310            mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
311            return mCanvas;
312        }
313    }
314
315    /**
316     * Posts the new contents of the {@link Canvas} to the surface and
317     * releases the {@link Canvas}.
318     *
319     * @param canvas The canvas previously obtained from {@link #lockCanvas}.
320     */
321    public void unlockCanvasAndPost(Canvas canvas) {
322        synchronized (mLock) {
323            checkNotReleasedLocked();
324
325            if (mHwuiContext != null) {
326                mHwuiContext.unlockAndPost(canvas);
327            } else {
328                unlockSwCanvasAndPost(canvas);
329            }
330        }
331    }
332
333    private void unlockSwCanvasAndPost(Canvas canvas) {
334        if (canvas != mCanvas) {
335            throw new IllegalArgumentException("canvas object must be the same instance that "
336                    + "was previously returned by lockCanvas");
337        }
338        if (mNativeObject != mLockedObject) {
339            Log.w(TAG, "WARNING: Surface's mNativeObject (0x" +
340                    Long.toHexString(mNativeObject) + ") != mLockedObject (0x" +
341                    Long.toHexString(mLockedObject) +")");
342        }
343        if (mLockedObject == 0) {
344            throw new IllegalStateException("Surface was not locked");
345        }
346        try {
347            nativeUnlockCanvasAndPost(mLockedObject, canvas);
348        } finally {
349            nativeRelease(mLockedObject);
350            mLockedObject = 0;
351        }
352    }
353
354    /**
355     * Gets a {@link Canvas} for drawing into this surface.
356     *
357     * After drawing into the provided {@link Canvas}, the caller must
358     * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
359     *
360     * Unlike {@link #lockCanvas(Rect)} this will return a hardware-accelerated
361     * canvas. See the <a href="{@docRoot}guide/topics/graphics/hardware-accel.html#unsupported">
362     * unsupported drawing operations</a> for a list of what is and isn't
363     * supported in a hardware-accelerated canvas. It is also required to
364     * fully cover the surface every time {@link #lockHardwareCanvas()} is
365     * called as the buffer is not preserved between frames. Partial updates
366     * are not supported.
367     *
368     * @return A canvas for drawing into the surface.
369     *
370     * @throws IllegalStateException If the canvas cannot be locked.
371     */
372    public Canvas lockHardwareCanvas() {
373        synchronized (mLock) {
374            checkNotReleasedLocked();
375            if (mHwuiContext == null) {
376                mHwuiContext = new HwuiContext();
377            }
378            return mHwuiContext.lockCanvas(
379                    nativeGetWidth(mNativeObject),
380                    nativeGetHeight(mNativeObject));
381        }
382    }
383
384    /**
385     * @deprecated This API has been removed and is not supported.  Do not use.
386     */
387    @Deprecated
388    public void unlockCanvas(Canvas canvas) {
389        throw new UnsupportedOperationException();
390    }
391
392    /**
393     * Sets the translator used to scale canvas's width/height in compatibility
394     * mode.
395     */
396    void setCompatibilityTranslator(Translator translator) {
397        if (translator != null) {
398            float appScale = translator.applicationScale;
399            mCompatibleMatrix = new Matrix();
400            mCompatibleMatrix.setScale(appScale, appScale);
401        }
402    }
403
404    /**
405     * Copy another surface to this one.  This surface now holds a reference
406     * to the same data as the original surface, and is -not- the owner.
407     * This is for use by the window manager when returning a window surface
408     * back from a client, converting it from the representation being managed
409     * by the window manager to the representation the client uses to draw
410     * in to it.
411     * @hide
412     */
413    public void copyFrom(SurfaceControl other) {
414        if (other == null) {
415            throw new IllegalArgumentException("other must not be null");
416        }
417
418        long surfaceControlPtr = other.mNativeObject;
419        if (surfaceControlPtr == 0) {
420            throw new NullPointerException(
421                    "SurfaceControl native object is null. Are you using a released SurfaceControl?");
422        }
423        long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
424
425        synchronized (mLock) {
426            if (mNativeObject != 0) {
427                nativeRelease(mNativeObject);
428            }
429            setNativeObjectLocked(newNativeObject);
430        }
431    }
432
433    /**
434     * This is intended to be used by {@link SurfaceView#updateWindow} only.
435     * @param other access is not thread safe
436     * @hide
437     * @deprecated
438     */
439    @Deprecated
440    public void transferFrom(Surface other) {
441        if (other == null) {
442            throw new IllegalArgumentException("other must not be null");
443        }
444        if (other != this) {
445            final long newPtr;
446            synchronized (other.mLock) {
447                newPtr = other.mNativeObject;
448                other.setNativeObjectLocked(0);
449            }
450
451            synchronized (mLock) {
452                if (mNativeObject != 0) {
453                    nativeRelease(mNativeObject);
454                }
455                setNativeObjectLocked(newPtr);
456            }
457        }
458    }
459
460    @Override
461    public int describeContents() {
462        return 0;
463    }
464
465    public void readFromParcel(Parcel source) {
466        if (source == null) {
467            throw new IllegalArgumentException("source must not be null");
468        }
469
470        synchronized (mLock) {
471            // nativeReadFromParcel() will either return mNativeObject, or
472            // create a new native Surface and return it after reducing
473            // the reference count on mNativeObject.  Either way, it is
474            // not necessary to call nativeRelease() here.
475            // NOTE: This must be kept synchronized with the native parceling code
476            // in frameworks/native/libs/Surface.cpp
477            mName = source.readString();
478            mIsSingleBuffered = source.readInt() != 0;
479            setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
480        }
481    }
482
483    @Override
484    public void writeToParcel(Parcel dest, int flags) {
485        if (dest == null) {
486            throw new IllegalArgumentException("dest must not be null");
487        }
488        synchronized (mLock) {
489            // NOTE: This must be kept synchronized with the native parceling code
490            // in frameworks/native/libs/Surface.cpp
491            dest.writeString(mName);
492            dest.writeInt(mIsSingleBuffered ? 1 : 0);
493            nativeWriteToParcel(mNativeObject, dest);
494        }
495        if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
496            release();
497        }
498    }
499
500    @Override
501    public String toString() {
502        synchronized (mLock) {
503            return "Surface(name=" + mName + ")/@0x" +
504                    Integer.toHexString(System.identityHashCode(this));
505        }
506    }
507
508    private void setNativeObjectLocked(long ptr) {
509        if (mNativeObject != ptr) {
510            if (mNativeObject == 0 && ptr != 0) {
511                mCloseGuard.open("release");
512            } else if (mNativeObject != 0 && ptr == 0) {
513                mCloseGuard.close();
514            }
515            mNativeObject = ptr;
516            mGenerationId += 1;
517            if (mHwuiContext != null) {
518                mHwuiContext.updateSurface();
519            }
520        }
521    }
522
523    private void checkNotReleasedLocked() {
524        if (mNativeObject == 0) {
525            throw new IllegalStateException("Surface has already been released.");
526        }
527    }
528
529    /**
530     * Allocate buffers ahead of time to avoid allocation delays during rendering
531     * @hide
532     */
533    public void allocateBuffers() {
534        synchronized (mLock) {
535            checkNotReleasedLocked();
536            nativeAllocateBuffers(mNativeObject);
537        }
538    }
539
540    /**
541     * Set the scaling mode to be used for this surfaces buffers
542     * @hide
543     */
544    void setScalingMode(@ScalingMode int scalingMode) {
545        synchronized (mLock) {
546            checkNotReleasedLocked();
547            int err = nativeSetScalingMode(mNativeObject, scalingMode);
548            if (err != 0) {
549                throw new IllegalArgumentException("Invalid scaling mode: " + scalingMode);
550            }
551        }
552    }
553
554    void forceScopedDisconnect() {
555        synchronized (mLock) {
556            checkNotReleasedLocked();
557            int err = nativeForceScopedDisconnect(mNativeObject);
558            if (err != 0) {
559                throw new RuntimeException("Failed to disconnect Surface instance (bad object?)");
560            }
561        }
562    }
563
564    /**
565     * Returns whether or not this Surface is backed by a single-buffered SurfaceTexture
566     * @hide
567     */
568    public boolean isSingleBuffered() {
569        return mIsSingleBuffered;
570    }
571
572    /**
573     * Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or
574     * when a SurfaceTexture could not successfully be allocated.
575     */
576    @SuppressWarnings("serial")
577    public static class OutOfResourcesException extends RuntimeException {
578        public OutOfResourcesException() {
579        }
580        public OutOfResourcesException(String name) {
581            super(name);
582        }
583    }
584
585    /**
586     * Returns a human readable representation of a rotation.
587     *
588     * @param rotation The rotation.
589     * @return The rotation symbolic name.
590     *
591     * @hide
592     */
593    public static String rotationToString(int rotation) {
594        switch (rotation) {
595            case Surface.ROTATION_0: {
596                return "ROTATION_0";
597            }
598            case Surface.ROTATION_90: {
599                return "ROATATION_90";
600            }
601            case Surface.ROTATION_180: {
602                return "ROATATION_180";
603            }
604            case Surface.ROTATION_270: {
605                return "ROATATION_270";
606            }
607            default: {
608                throw new IllegalArgumentException("Invalid rotation: " + rotation);
609            }
610        }
611    }
612
613    /**
614     * A Canvas class that can handle the compatibility mode.
615     * This does two things differently.
616     * <ul>
617     * <li>Returns the width and height of the target metrics, rather than
618     * native. For example, the canvas returns 320x480 even if an app is running
619     * in WVGA high density.
620     * <li>Scales the matrix in setMatrix by the application scale, except if
621     * the matrix looks like obtained from getMatrix. This is a hack to handle
622     * the case that an application uses getMatrix to keep the original matrix,
623     * set matrix of its own, then set the original matrix back. There is no
624     * perfect solution that works for all cases, and there are a lot of cases
625     * that this model does not work, but we hope this works for many apps.
626     * </ul>
627     */
628    private final class CompatibleCanvas extends Canvas {
629        // A temp matrix to remember what an application obtained via {@link getMatrix}
630        private Matrix mOrigMatrix = null;
631
632        @Override
633        public void setMatrix(Matrix matrix) {
634            if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) {
635                // don't scale the matrix if it's not compatibility mode, or
636                // the matrix was obtained from getMatrix.
637                super.setMatrix(matrix);
638            } else {
639                Matrix m = new Matrix(mCompatibleMatrix);
640                m.preConcat(matrix);
641                super.setMatrix(m);
642            }
643        }
644
645        @SuppressWarnings("deprecation")
646        @Override
647        public void getMatrix(Matrix m) {
648            super.getMatrix(m);
649            if (mOrigMatrix == null) {
650                mOrigMatrix = new Matrix();
651            }
652            mOrigMatrix.set(m);
653        }
654    }
655
656    private final class HwuiContext {
657        private final RenderNode mRenderNode;
658        private long mHwuiRenderer;
659        private DisplayListCanvas mCanvas;
660
661        HwuiContext() {
662            mRenderNode = RenderNode.create("HwuiCanvas", null);
663            mRenderNode.setClipToBounds(false);
664            mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject);
665        }
666
667        Canvas lockCanvas(int width, int height) {
668            if (mCanvas != null) {
669                throw new IllegalStateException("Surface was already locked!");
670            }
671            mCanvas = mRenderNode.start(width, height);
672            return mCanvas;
673        }
674
675        void unlockAndPost(Canvas canvas) {
676            if (canvas != mCanvas) {
677                throw new IllegalArgumentException("canvas object must be the same instance that "
678                        + "was previously returned by lockCanvas");
679            }
680            mRenderNode.end(mCanvas);
681            mCanvas = null;
682            nHwuiDraw(mHwuiRenderer);
683        }
684
685        void updateSurface() {
686            nHwuiSetSurface(mHwuiRenderer, mNativeObject);
687        }
688
689        void destroy() {
690            if (mHwuiRenderer != 0) {
691                nHwuiDestroy(mHwuiRenderer);
692                mHwuiRenderer = 0;
693            }
694        }
695    }
696
697    private static native long nHwuiCreate(long rootNode, long surface);
698    private static native void nHwuiSetSurface(long renderer, long surface);
699    private static native void nHwuiDraw(long renderer);
700    private static native void nHwuiDestroy(long renderer);
701}
702