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