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 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