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