Home | History | Annotate | Download | only in view
      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 
     17 package android.view;
     18 
     19 import android.content.res.CompatibilityInfo.Translator;
     20 import android.graphics.Canvas;
     21 import android.graphics.Matrix;
     22 import android.graphics.Rect;
     23 import android.graphics.SurfaceTexture;
     24 import android.os.Parcel;
     25 import android.os.Parcelable;
     26 import android.util.Log;
     27 import dalvik.system.CloseGuard;
     28 
     29 /**
     30  * Handle onto a raw buffer that is being managed by the screen compositor.
     31  */
     32 public 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