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      * @hide
    326      */
    327     public Canvas lockHardwareCanvas() {
    328         synchronized (mLock) {
    329             checkNotReleasedLocked();
    330             if (mHwuiContext == null) {
    331                 mHwuiContext = new HwuiContext();
    332             }
    333             return mHwuiContext.lockCanvas(
    334                     nativeGetWidth(mNativeObject),
    335                     nativeGetHeight(mNativeObject));
    336         }
    337     }
    338 
    339     /**
    340      * @deprecated This API has been removed and is not supported.  Do not use.
    341      */
    342     @Deprecated
    343     public void unlockCanvas(Canvas canvas) {
    344         throw new UnsupportedOperationException();
    345     }
    346 
    347     /**
    348      * Sets the translator used to scale canvas's width/height in compatibility
    349      * mode.
    350      */
    351     void setCompatibilityTranslator(Translator translator) {
    352         if (translator != null) {
    353             float appScale = translator.applicationScale;
    354             mCompatibleMatrix = new Matrix();
    355             mCompatibleMatrix.setScale(appScale, appScale);
    356         }
    357     }
    358 
    359     /**
    360      * Copy another surface to this one.  This surface now holds a reference
    361      * to the same data as the original surface, and is -not- the owner.
    362      * This is for use by the window manager when returning a window surface
    363      * back from a client, converting it from the representation being managed
    364      * by the window manager to the representation the client uses to draw
    365      * in to it.
    366      * @hide
    367      */
    368     public void copyFrom(SurfaceControl other) {
    369         if (other == null) {
    370             throw new IllegalArgumentException("other must not be null");
    371         }
    372 
    373         long surfaceControlPtr = other.mNativeObject;
    374         if (surfaceControlPtr == 0) {
    375             throw new NullPointerException(
    376                     "SurfaceControl native object is null. Are you using a released SurfaceControl?");
    377         }
    378         long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
    379 
    380         synchronized (mLock) {
    381             if (mNativeObject != 0) {
    382                 nativeRelease(mNativeObject);
    383             }
    384             setNativeObjectLocked(newNativeObject);
    385         }
    386     }
    387 
    388     /**
    389      * This is intended to be used by {@link SurfaceView#updateWindow} only.
    390      * @param other access is not thread safe
    391      * @hide
    392      * @deprecated
    393      */
    394     @Deprecated
    395     public void transferFrom(Surface other) {
    396         if (other == null) {
    397             throw new IllegalArgumentException("other must not be null");
    398         }
    399         if (other != this) {
    400             final long newPtr;
    401             synchronized (other.mLock) {
    402                 newPtr = other.mNativeObject;
    403                 other.setNativeObjectLocked(0);
    404             }
    405 
    406             synchronized (mLock) {
    407                 if (mNativeObject != 0) {
    408                     nativeRelease(mNativeObject);
    409                 }
    410                 setNativeObjectLocked(newPtr);
    411             }
    412         }
    413     }
    414 
    415     @Override
    416     public int describeContents() {
    417         return 0;
    418     }
    419 
    420     public void readFromParcel(Parcel source) {
    421         if (source == null) {
    422             throw new IllegalArgumentException("source must not be null");
    423         }
    424 
    425         synchronized (mLock) {
    426             // nativeReadFromParcel() will either return mNativeObject, or
    427             // create a new native Surface and return it after reducing
    428             // the reference count on mNativeObject.  Either way, it is
    429             // not necessary to call nativeRelease() here.
    430             mName = source.readString();
    431             setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
    432         }
    433     }
    434 
    435     @Override
    436     public void writeToParcel(Parcel dest, int flags) {
    437         if (dest == null) {
    438             throw new IllegalArgumentException("dest must not be null");
    439         }
    440         synchronized (mLock) {
    441             dest.writeString(mName);
    442             nativeWriteToParcel(mNativeObject, dest);
    443         }
    444         if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
    445             release();
    446         }
    447     }
    448 
    449     @Override
    450     public String toString() {
    451         synchronized (mLock) {
    452             return "Surface(name=" + mName + ")/@0x" +
    453                     Integer.toHexString(System.identityHashCode(this));
    454         }
    455     }
    456 
    457     private void setNativeObjectLocked(long ptr) {
    458         if (mNativeObject != ptr) {
    459             if (mNativeObject == 0 && ptr != 0) {
    460                 mCloseGuard.open("release");
    461             } else if (mNativeObject != 0 && ptr == 0) {
    462                 mCloseGuard.close();
    463             }
    464             mNativeObject = ptr;
    465             mGenerationId += 1;
    466             if (mHwuiContext != null) {
    467                 mHwuiContext.updateSurface();
    468             }
    469         }
    470     }
    471 
    472     private void checkNotReleasedLocked() {
    473         if (mNativeObject == 0) {
    474             throw new IllegalStateException("Surface has already been released.");
    475         }
    476     }
    477 
    478     /**
    479      * Allocate buffers ahead of time to avoid allocation delays during rendering
    480      * @hide
    481      */
    482     public void allocateBuffers() {
    483         synchronized (mLock) {
    484             checkNotReleasedLocked();
    485             nativeAllocateBuffers(mNativeObject);
    486         }
    487     }
    488 
    489     /**
    490      * Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or
    491      * when a SurfaceTexture could not successfully be allocated.
    492      */
    493     @SuppressWarnings("serial")
    494     public static class OutOfResourcesException extends RuntimeException {
    495         public OutOfResourcesException() {
    496         }
    497         public OutOfResourcesException(String name) {
    498             super(name);
    499         }
    500     }
    501 
    502     /**
    503      * Returns a human readable representation of a rotation.
    504      *
    505      * @param rotation The rotation.
    506      * @return The rotation symbolic name.
    507      *
    508      * @hide
    509      */
    510     public static String rotationToString(int rotation) {
    511         switch (rotation) {
    512             case Surface.ROTATION_0: {
    513                 return "ROTATION_0";
    514             }
    515             case Surface.ROTATION_90: {
    516                 return "ROATATION_90";
    517             }
    518             case Surface.ROTATION_180: {
    519                 return "ROATATION_180";
    520             }
    521             case Surface.ROTATION_270: {
    522                 return "ROATATION_270";
    523             }
    524             default: {
    525                 throw new IllegalArgumentException("Invalid rotation: " + rotation);
    526             }
    527         }
    528     }
    529 
    530     /**
    531      * A Canvas class that can handle the compatibility mode.
    532      * This does two things differently.
    533      * <ul>
    534      * <li>Returns the width and height of the target metrics, rather than
    535      * native. For example, the canvas returns 320x480 even if an app is running
    536      * in WVGA high density.
    537      * <li>Scales the matrix in setMatrix by the application scale, except if
    538      * the matrix looks like obtained from getMatrix. This is a hack to handle
    539      * the case that an application uses getMatrix to keep the original matrix,
    540      * set matrix of its own, then set the original matrix back. There is no
    541      * perfect solution that works for all cases, and there are a lot of cases
    542      * that this model does not work, but we hope this works for many apps.
    543      * </ul>
    544      */
    545     private final class CompatibleCanvas extends Canvas {
    546         // A temp matrix to remember what an application obtained via {@link getMatrix}
    547         private Matrix mOrigMatrix = null;
    548 
    549         @Override
    550         public void setMatrix(Matrix matrix) {
    551             if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) {
    552                 // don't scale the matrix if it's not compatibility mode, or
    553                 // the matrix was obtained from getMatrix.
    554                 super.setMatrix(matrix);
    555             } else {
    556                 Matrix m = new Matrix(mCompatibleMatrix);
    557                 m.preConcat(matrix);
    558                 super.setMatrix(m);
    559             }
    560         }
    561 
    562         @SuppressWarnings("deprecation")
    563         @Override
    564         public void getMatrix(Matrix m) {
    565             super.getMatrix(m);
    566             if (mOrigMatrix == null) {
    567                 mOrigMatrix = new Matrix();
    568             }
    569             mOrigMatrix.set(m);
    570         }
    571     }
    572 
    573     private final class HwuiContext {
    574         private final RenderNode mRenderNode;
    575         private long mHwuiRenderer;
    576         private HardwareCanvas mCanvas;
    577 
    578         HwuiContext() {
    579             mRenderNode = RenderNode.create("HwuiCanvas", null);
    580             mRenderNode.setClipToBounds(false);
    581             mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject);
    582         }
    583 
    584         Canvas lockCanvas(int width, int height) {
    585             if (mCanvas != null) {
    586                 throw new IllegalStateException("Surface was already locked!");
    587             }
    588             mCanvas = mRenderNode.start(width, height);
    589             return mCanvas;
    590         }
    591 
    592         void unlockAndPost(Canvas canvas) {
    593             if (canvas != mCanvas) {
    594                 throw new IllegalArgumentException("canvas object must be the same instance that "
    595                         + "was previously returned by lockCanvas");
    596             }
    597             mRenderNode.end(mCanvas);
    598             mCanvas = null;
    599             nHwuiDraw(mHwuiRenderer);
    600         }
    601 
    602         void updateSurface() {
    603             nHwuiSetSurface(mHwuiRenderer, mNativeObject);
    604         }
    605 
    606         void destroy() {
    607             if (mHwuiRenderer != 0) {
    608                 nHwuiDestroy(mHwuiRenderer);
    609                 mHwuiRenderer = 0;
    610             }
    611         }
    612     }
    613 
    614     private static native long nHwuiCreate(long rootNode, long surface);
    615     private static native void nHwuiSetSurface(long renderer, long surface);
    616     private static native void nHwuiDraw(long renderer);
    617     private static native void nHwuiDestroy(long renderer);
    618 }
    619