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