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