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.GraphicBuffer;
     23 import android.graphics.Matrix;
     24 import android.graphics.Rect;
     25 import android.graphics.SurfaceTexture;
     26 import android.os.Parcel;
     27 import android.os.Parcelable;
     28 import android.util.Log;
     29 
     30 import dalvik.system.CloseGuard;
     31 
     32 import java.lang.annotation.Retention;
     33 import 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  */
     50 public 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