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