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