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 
nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)53     private static native long nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
54             throws OutOfResourcesException;
55 
nativeCreateFromSurfaceControl(long surfaceControlNativeObject)56     private static native long nativeCreateFromSurfaceControl(long surfaceControlNativeObject);
nativeGetFromSurfaceControl(long surfaceControlNativeObject)57     private static native long nativeGetFromSurfaceControl(long surfaceControlNativeObject);
58 
nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)59     private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)
60             throws OutOfResourcesException;
nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas)61     private static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas);
62 
nativeRelease(long nativeObject)63     private static native void nativeRelease(long nativeObject);
nativeIsValid(long nativeObject)64     private static native boolean nativeIsValid(long nativeObject);
nativeIsConsumerRunningBehind(long nativeObject)65     private static native boolean nativeIsConsumerRunningBehind(long nativeObject);
nativeReadFromParcel(long nativeObject, Parcel source)66     private static native long nativeReadFromParcel(long nativeObject, Parcel source);
nativeWriteToParcel(long nativeObject, Parcel dest)67     private static native void nativeWriteToParcel(long nativeObject, Parcel dest);
68 
nativeAllocateBuffers(long nativeObject)69     private static native void nativeAllocateBuffers(long nativeObject);
70 
nativeGetWidth(long nativeObject)71     private static native int nativeGetWidth(long nativeObject);
nativeGetHeight(long nativeObject)72     private static native int nativeGetHeight(long nativeObject);
73 
nativeGetNextFrameNumber(long nativeObject)74     private static native long nativeGetNextFrameNumber(long nativeObject);
nativeSetScalingMode(long nativeObject, int scalingMode)75     private static native int nativeSetScalingMode(long nativeObject, int scalingMode);
nativeSetBuffersTransform(long nativeObject, long transform)76     private static native void nativeSetBuffersTransform(long nativeObject, long transform);
nativeForceScopedDisconnect(long nativeObject)77     private static native int nativeForceScopedDisconnect(long nativeObject);
nativeAttachAndQueueBuffer(long nativeObject, GraphicBuffer buffer)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      */
Surface()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      */
Surface(SurfaceTexture surfaceTexture)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() */
Surface(long nativeObject)188     private Surface(long nativeObject) {
189         synchronized (mLock) {
190             setNativeObjectLocked(nativeObject);
191         }
192     }
193 
194     @Override
finalize()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      */
release()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      */
destroy()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      */
isValid()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      */
getGenerationId()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      */
getNextFrameNumber()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      */
isConsumerRunningBehind()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      */
lockCanvas(Rect inOutDirty)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      */
unlockCanvasAndPost(Canvas canvas)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 
unlockSwCanvasAndPost(Canvas canvas)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      */
lockHardwareCanvas()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
unlockCanvas(Canvas canvas)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      */
setCompatibilityTranslator(Translator translator)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      */
copyFrom(SurfaceControl other)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      */
createFrom(SurfaceControl other)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
transferFrom(Surface other)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
describeContents()500     public int describeContents() {
501         return 0;
502     }
503 
readFromParcel(Parcel source)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
writeToParcel(Parcel dest, int flags)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
toString()540     public String toString() {
541         synchronized (mLock) {
542             return "Surface(name=" + mName + ")/@0x" +
543                     Integer.toHexString(System.identityHashCode(this));
544         }
545     }
546 
setNativeObjectLocked(long ptr)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 
checkNotReleasedLocked()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      */
allocateBuffers()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      */
setScalingMode(@calingMode int scalingMode)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 
forceScopedDisconnect()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      */
attachAndQueueBuffer(GraphicBuffer buffer)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      */
isSingleBuffered()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 {
OutOfResourcesException()632         public OutOfResourcesException() {
633         }
OutOfResourcesException(String name)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      */
rotationToString(int rotation)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
setMatrix(Matrix matrix)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
getMatrix(Matrix m)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 
HwuiContext()715         HwuiContext() {
716             mRenderNode = RenderNode.create("HwuiCanvas", null);
717             mRenderNode.setClipToBounds(false);
718             mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject);
719         }
720 
lockCanvas(int width, int height)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 
unlockAndPost(Canvas canvas)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 
updateSurface()739         void updateSurface() {
740             nHwuiSetSurface(mHwuiRenderer, mNativeObject);
741         }
742 
destroy()743         void destroy() {
744             if (mHwuiRenderer != 0) {
745                 nHwuiDestroy(mHwuiRenderer);
746                 mHwuiRenderer = 0;
747             }
748         }
749     }
750 
nHwuiCreate(long rootNode, long surface)751     private static native long nHwuiCreate(long rootNode, long surface);
nHwuiSetSurface(long renderer, long surface)752     private static native void nHwuiSetSurface(long renderer, long surface);
nHwuiDraw(long renderer)753     private static native void nHwuiDraw(long renderer);
nHwuiDestroy(long renderer)754     private static native void nHwuiDestroy(long renderer);
755 }
756