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