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.annotation.IntDef;
     20 import android.content.res.CompatibilityInfo.Translator;
     21 import android.graphics.Canvas;
     22 import android.graphics.Matrix;
     23 import android.graphics.Rect;
     24 import android.graphics.SurfaceTexture;
     25 import android.os.Parcel;
     26 import android.os.Parcelable;
     27 import android.util.Log;
     28 
     29 import java.lang.annotation.Retention;
     30 import java.lang.annotation.RetentionPolicy;
     31 
     32 import dalvik.system.CloseGuard;
     33 
     34 /**
     35  * Handle onto a raw buffer that is being managed by the screen compositor.
     36  */
     37 public 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