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