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.content.res.CompatibilityInfo.Translator;
20import android.graphics.Canvas;
21import android.graphics.Matrix;
22import android.graphics.Rect;
23import android.graphics.SurfaceTexture;
24import android.os.Parcel;
25import android.os.Parcelable;
26import android.util.Log;
27import dalvik.system.CloseGuard;
28
29/**
30 * Handle onto a raw buffer that is being managed by the screen compositor.
31 */
32public class Surface implements Parcelable {
33    private static final String TAG = "Surface";
34
35    private static native int nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
36            throws OutOfResourcesException;
37    private static native int nativeCreateFromSurfaceControl(int surfaceControlNativeObject);
38
39    private static native int nativeLockCanvas(int nativeObject, Canvas canvas, Rect dirty)
40            throws OutOfResourcesException;
41    private static native void nativeUnlockCanvasAndPost(int nativeObject, Canvas canvas);
42
43    private static native void nativeRelease(int nativeObject);
44    private static native boolean nativeIsValid(int nativeObject);
45    private static native boolean nativeIsConsumerRunningBehind(int nativeObject);
46    private static native int nativeReadFromParcel(int nativeObject, Parcel source);
47    private static native void nativeWriteToParcel(int nativeObject, Parcel dest);
48
49    public static final Parcelable.Creator<Surface> CREATOR =
50            new Parcelable.Creator<Surface>() {
51        @Override
52        public Surface createFromParcel(Parcel source) {
53            try {
54                Surface s = new Surface();
55                s.readFromParcel(source);
56                return s;
57            } catch (Exception e) {
58                Log.e(TAG, "Exception creating surface from parcel", e);
59                return null;
60            }
61        }
62
63        @Override
64        public Surface[] newArray(int size) {
65            return new Surface[size];
66        }
67    };
68
69    private final CloseGuard mCloseGuard = CloseGuard.get();
70
71    // Guarded state.
72    final Object mLock = new Object(); // protects the native state
73    private String mName;
74    int mNativeObject; // package scope only for SurfaceControl access
75    private int mLockedObject;
76    private int mGenerationId; // incremented each time mNativeObject changes
77    private final Canvas mCanvas = new CompatibleCanvas();
78
79    // A matrix to scale the matrix set by application. This is set to null for
80    // non compatibility mode.
81    private Matrix mCompatibleMatrix;
82
83    /**
84     * Rotation constant: 0 degree rotation (natural orientation)
85     */
86    public static final int ROTATION_0 = 0;
87
88    /**
89     * Rotation constant: 90 degree rotation.
90     */
91    public static final int ROTATION_90 = 1;
92
93    /**
94     * Rotation constant: 180 degree rotation.
95     */
96    public static final int ROTATION_180 = 2;
97
98    /**
99     * Rotation constant: 270 degree rotation.
100     */
101    public static final int ROTATION_270 = 3;
102
103    /**
104     * Create an empty surface, which will later be filled in by readFromParcel().
105     * @hide
106     */
107    public Surface() {
108    }
109
110    /**
111     * Create Surface from a {@link SurfaceTexture}.
112     *
113     * Images drawn to the Surface will be made available to the {@link
114     * SurfaceTexture}, which can attach them to an OpenGL ES texture via {@link
115     * SurfaceTexture#updateTexImage}.
116     *
117     * @param surfaceTexture The {@link SurfaceTexture} that is updated by this
118     * Surface.
119     * @throws OutOfResourcesException if the surface could not be created.
120     */
121    public Surface(SurfaceTexture surfaceTexture) {
122        if (surfaceTexture == null) {
123            throw new IllegalArgumentException("surfaceTexture must not be null");
124        }
125
126        synchronized (mLock) {
127            mName = surfaceTexture.toString();
128            setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
129        }
130    }
131
132    /* called from android_view_Surface_createFromIGraphicBufferProducer() */
133    private Surface(int nativeObject) {
134        synchronized (mLock) {
135            setNativeObjectLocked(nativeObject);
136        }
137    }
138
139    @Override
140    protected void finalize() throws Throwable {
141        try {
142            if (mCloseGuard != null) {
143                mCloseGuard.warnIfOpen();
144            }
145            release();
146        } finally {
147            super.finalize();
148        }
149    }
150
151    /**
152     * Release the local reference to the server-side surface.
153     * Always call release() when you're done with a Surface.
154     * This will make the surface invalid.
155     */
156    public void release() {
157        synchronized (mLock) {
158            if (mNativeObject != 0) {
159                nativeRelease(mNativeObject);
160                setNativeObjectLocked(0);
161            }
162        }
163    }
164
165    /**
166     * Free all server-side state associated with this surface and
167     * release this object's reference.  This method can only be
168     * called from the process that created the service.
169     * @hide
170     */
171    public void destroy() {
172        release();
173    }
174
175    /**
176     * Returns true if this object holds a valid surface.
177     *
178     * @return True if it holds a physical surface, so lockCanvas() will succeed.
179     * Otherwise returns false.
180     */
181    public boolean isValid() {
182        synchronized (mLock) {
183            if (mNativeObject == 0) return false;
184            return nativeIsValid(mNativeObject);
185        }
186    }
187
188    /**
189     * Gets the generation number of this surface, incremented each time
190     * the native surface contained within this object changes.
191     *
192     * @return The current generation number.
193     * @hide
194     */
195    public int getGenerationId() {
196        synchronized (mLock) {
197            return mGenerationId;
198        }
199    }
200
201    /**
202     * Returns true if the consumer of this Surface is running behind the producer.
203     *
204     * @return True if the consumer is more than one buffer ahead of the producer.
205     * @hide
206     */
207    public boolean isConsumerRunningBehind() {
208        synchronized (mLock) {
209            checkNotReleasedLocked();
210            return nativeIsConsumerRunningBehind(mNativeObject);
211        }
212    }
213
214    /**
215     * Gets a {@link Canvas} for drawing into this surface.
216     *
217     * After drawing into the provided {@link Canvas}, the caller must
218     * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
219     *
220     * @param inOutDirty A rectangle that represents the dirty region that the caller wants
221     * to redraw.  This function may choose to expand the dirty rectangle if for example
222     * the surface has been resized or if the previous contents of the surface were
223     * not available.  The caller must redraw the entire dirty region as represented
224     * by the contents of the inOutDirty rectangle upon return from this function.
225     * The caller may also pass <code>null</code> instead, in the case where the
226     * entire surface should be redrawn.
227     * @return A canvas for drawing into the surface.
228     *
229     * @throws IllegalArgumentException If the inOutDirty rectangle is not valid.
230     * @throws OutOfResourcesException If the canvas cannot be locked.
231     */
232    public Canvas lockCanvas(Rect inOutDirty)
233            throws Surface.OutOfResourcesException, IllegalArgumentException {
234        synchronized (mLock) {
235            checkNotReleasedLocked();
236            if (mLockedObject != 0) {
237                // Ideally, nativeLockCanvas() would throw in this situation and prevent the
238                // double-lock, but that won't happen if mNativeObject was updated.  We can't
239                // abandon the old mLockedObject because it might still be in use, so instead
240                // we just refuse to re-lock the Surface.
241                throw new IllegalStateException("Surface was already locked");
242            }
243            mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
244            return mCanvas;
245        }
246    }
247
248    /**
249     * Posts the new contents of the {@link Canvas} to the surface and
250     * releases the {@link Canvas}.
251     *
252     * @param canvas The canvas previously obtained from {@link #lockCanvas}.
253     */
254    public void unlockCanvasAndPost(Canvas canvas) {
255        if (canvas != mCanvas) {
256            throw new IllegalArgumentException("canvas object must be the same instance that "
257                    + "was previously returned by lockCanvas");
258        }
259
260        synchronized (mLock) {
261            checkNotReleasedLocked();
262            if (mNativeObject != mLockedObject) {
263                Log.w(TAG, "WARNING: Surface's mNativeObject (0x" +
264                        Integer.toHexString(mNativeObject) + ") != mLockedObject (0x" +
265                        Integer.toHexString(mLockedObject) +")");
266            }
267            if (mLockedObject == 0) {
268                throw new IllegalStateException("Surface was not locked");
269            }
270            nativeUnlockCanvasAndPost(mLockedObject, canvas);
271            nativeRelease(mLockedObject);
272            mLockedObject = 0;
273        }
274    }
275
276    /**
277     * @deprecated This API has been removed and is not supported.  Do not use.
278     */
279    @Deprecated
280    public void unlockCanvas(Canvas canvas) {
281        throw new UnsupportedOperationException();
282    }
283
284    /**
285     * Sets the translator used to scale canvas's width/height in compatibility
286     * mode.
287     */
288    void setCompatibilityTranslator(Translator translator) {
289        if (translator != null) {
290            float appScale = translator.applicationScale;
291            mCompatibleMatrix = new Matrix();
292            mCompatibleMatrix.setScale(appScale, appScale);
293        }
294    }
295
296    /**
297     * Copy another surface to this one.  This surface now holds a reference
298     * to the same data as the original surface, and is -not- the owner.
299     * This is for use by the window manager when returning a window surface
300     * back from a client, converting it from the representation being managed
301     * by the window manager to the representation the client uses to draw
302     * in to it.
303     * @hide
304     */
305    public void copyFrom(SurfaceControl other) {
306        if (other == null) {
307            throw new IllegalArgumentException("other must not be null");
308        }
309
310        int surfaceControlPtr = other.mNativeObject;
311        if (surfaceControlPtr == 0) {
312            throw new NullPointerException(
313                    "SurfaceControl native object is null. Are you using a released SurfaceControl?");
314        }
315        int newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
316
317        synchronized (mLock) {
318            if (mNativeObject != 0) {
319                nativeRelease(mNativeObject);
320            }
321            setNativeObjectLocked(newNativeObject);
322        }
323    }
324
325    /**
326     * This is intended to be used by {@link SurfaceView#updateWindow} only.
327     * @param other access is not thread safe
328     * @hide
329     * @deprecated
330     */
331    @Deprecated
332    public void transferFrom(Surface other) {
333        if (other == null) {
334            throw new IllegalArgumentException("other must not be null");
335        }
336        if (other != this) {
337            final int newPtr;
338            synchronized (other.mLock) {
339                newPtr = other.mNativeObject;
340                other.setNativeObjectLocked(0);
341            }
342
343            synchronized (mLock) {
344                if (mNativeObject != 0) {
345                    nativeRelease(mNativeObject);
346                }
347                setNativeObjectLocked(newPtr);
348            }
349        }
350    }
351
352    @Override
353    public int describeContents() {
354        return 0;
355    }
356
357    public void readFromParcel(Parcel source) {
358        if (source == null) {
359            throw new IllegalArgumentException("source must not be null");
360        }
361
362        synchronized (mLock) {
363            // nativeReadFromParcel() will either return mNativeObject, or
364            // create a new native Surface and return it after reducing
365            // the reference count on mNativeObject.  Either way, it is
366            // not necessary to call nativeRelease() here.
367            mName = source.readString();
368            setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
369        }
370    }
371
372    @Override
373    public void writeToParcel(Parcel dest, int flags) {
374        if (dest == null) {
375            throw new IllegalArgumentException("dest must not be null");
376        }
377        synchronized (mLock) {
378            dest.writeString(mName);
379            nativeWriteToParcel(mNativeObject, dest);
380        }
381        if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
382            release();
383        }
384    }
385
386    @Override
387    public String toString() {
388        synchronized (mLock) {
389            return "Surface(name=" + mName + ")/@0x" +
390                    Integer.toHexString(System.identityHashCode(this));
391        }
392    }
393
394    private void setNativeObjectLocked(int ptr) {
395        if (mNativeObject != ptr) {
396            if (mNativeObject == 0 && ptr != 0) {
397                mCloseGuard.open("release");
398            } else if (mNativeObject != 0 && ptr == 0) {
399                mCloseGuard.close();
400            }
401            mNativeObject = ptr;
402            mGenerationId += 1;
403        }
404    }
405
406    private void checkNotReleasedLocked() {
407        if (mNativeObject == 0) {
408            throw new IllegalStateException("Surface has already been released.");
409        }
410    }
411
412    /**
413     * Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or
414     * when a SurfaceTexture could not successfully be allocated.
415     */
416    @SuppressWarnings("serial")
417    public static class OutOfResourcesException extends RuntimeException {
418        public OutOfResourcesException() {
419        }
420        public OutOfResourcesException(String name) {
421            super(name);
422        }
423    }
424
425    /**
426     * Returns a human readable representation of a rotation.
427     *
428     * @param rotation The rotation.
429     * @return The rotation symbolic name.
430     *
431     * @hide
432     */
433    public static String rotationToString(int rotation) {
434        switch (rotation) {
435            case Surface.ROTATION_0: {
436                return "ROTATION_0";
437            }
438            case Surface.ROTATION_90: {
439                return "ROATATION_90";
440            }
441            case Surface.ROTATION_180: {
442                return "ROATATION_180";
443            }
444            case Surface.ROTATION_270: {
445                return "ROATATION_270";
446            }
447            default: {
448                throw new IllegalArgumentException("Invalid rotation: " + rotation);
449            }
450        }
451    }
452
453    /**
454     * A Canvas class that can handle the compatibility mode.
455     * This does two things differently.
456     * <ul>
457     * <li>Returns the width and height of the target metrics, rather than
458     * native. For example, the canvas returns 320x480 even if an app is running
459     * in WVGA high density.
460     * <li>Scales the matrix in setMatrix by the application scale, except if
461     * the matrix looks like obtained from getMatrix. This is a hack to handle
462     * the case that an application uses getMatrix to keep the original matrix,
463     * set matrix of its own, then set the original matrix back. There is no
464     * perfect solution that works for all cases, and there are a lot of cases
465     * that this model does not work, but we hope this works for many apps.
466     * </ul>
467     */
468    private final class CompatibleCanvas extends Canvas {
469        // A temp matrix to remember what an application obtained via {@link getMatrix}
470        private Matrix mOrigMatrix = null;
471
472        @Override
473        public void setMatrix(Matrix matrix) {
474            if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) {
475                // don't scale the matrix if it's not compatibility mode, or
476                // the matrix was obtained from getMatrix.
477                super.setMatrix(matrix);
478            } else {
479                Matrix m = new Matrix(mCompatibleMatrix);
480                m.preConcat(matrix);
481                super.setMatrix(m);
482            }
483        }
484
485        @SuppressWarnings("deprecation")
486        @Override
487        public void getMatrix(Matrix m) {
488            super.getMatrix(m);
489            if (mOrigMatrix == null) {
490                mOrigMatrix = new Matrix();
491            }
492            mOrigMatrix.set(m);
493        }
494    }
495}
496