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 dalvik.system.CloseGuard;
20
21import android.content.res.CompatibilityInfo.Translator;
22import android.graphics.Bitmap;
23import android.graphics.Canvas;
24import android.graphics.Matrix;
25import android.graphics.Rect;
26import android.graphics.Region;
27import android.graphics.SurfaceTexture;
28import android.os.IBinder;
29import android.os.Parcelable;
30import android.os.Parcel;
31import android.os.SystemProperties;
32import android.util.Log;
33
34/**
35 * Handle onto a raw buffer that is being managed by the screen compositor.
36 */
37public class Surface implements Parcelable {
38    private static final String TAG = "Surface";
39
40    private static final boolean HEADLESS = "1".equals(
41        SystemProperties.get("ro.config.headless", "0"));
42
43    public static final Parcelable.Creator<Surface> CREATOR =
44            new Parcelable.Creator<Surface>() {
45        public Surface createFromParcel(Parcel source) {
46            try {
47                Surface s = new Surface();
48                s.readFromParcel(source);
49                return s;
50            } catch (Exception e) {
51                Log.e(TAG, "Exception creating surface from parcel", e);
52                return null;
53            }
54        }
55
56        public Surface[] newArray(int size) {
57            return new Surface[size];
58        }
59    };
60
61    /**
62     * Rotation constant: 0 degree rotation (natural orientation)
63     */
64    public static final int ROTATION_0 = 0;
65
66    /**
67     * Rotation constant: 90 degree rotation.
68     */
69    public static final int ROTATION_90 = 1;
70
71    /**
72     * Rotation constant: 180 degree rotation.
73     */
74    public static final int ROTATION_180 = 2;
75
76    /**
77     * Rotation constant: 270 degree rotation.
78     */
79    public static final int ROTATION_270 = 3;
80
81    /* built-in physical display ids (keep in sync with ISurfaceComposer.h)
82     * these are different from the logical display ids used elsewhere in the framework */
83
84    /**
85     * Built-in physical display id: Main display.
86     * Use only with {@link #getBuiltInDisplay()}.
87     * @hide
88     */
89    public static final int BUILT_IN_DISPLAY_ID_MAIN = 0;
90
91    /**
92     * Built-in physical display id: Attached HDMI display.
93     * Use only with {@link #getBuiltInDisplay()}.
94     * @hide
95     */
96    public static final int BUILT_IN_DISPLAY_ID_HDMI = 1;
97
98    /* flags used in constructor (keep in sync with ISurfaceComposerClient.h) */
99
100    /**
101     * Surface creation flag: Surface is created hidden
102     * @hide */
103    public static final int HIDDEN = 0x00000004;
104
105    /**
106     * Surface creation flag: The surface contains secure content, special
107     * measures will be taken to disallow the surface's content to be copied
108     * from another process. In particular, screenshots and VNC servers will
109     * be disabled, but other measures can take place, for instance the
110     * surface might not be hardware accelerated.
111     * @hide
112     */
113    public static final int SECURE = 0x00000080;
114
115    /**
116     * Surface creation flag: Creates a surface where color components are interpreted
117     * as "non pre-multiplied" by their alpha channel. Of course this flag is
118     * meaningless for surfaces without an alpha channel. By default
119     * surfaces are pre-multiplied, which means that each color component is
120     * already multiplied by its alpha value. In this case the blending
121     * equation used is:
122     *
123     *    DEST = SRC + DEST * (1-SRC_ALPHA)
124     *
125     * By contrast, non pre-multiplied surfaces use the following equation:
126     *
127     *    DEST = SRC * SRC_ALPHA * DEST * (1-SRC_ALPHA)
128     *
129     * pre-multiplied surfaces must always be used if transparent pixels are
130     * composited on top of each-other into the surface. A pre-multiplied
131     * surface can never lower the value of the alpha component of a given
132     * pixel.
133     *
134     * In some rare situations, a non pre-multiplied surface is preferable.
135     * @hide
136     */
137    public static final int NON_PREMULTIPLIED = 0x00000100;
138
139    /**
140     * Surface creation flag: Indicates that the surface must be considered opaque,
141     * even if its pixel format is set to translucent. This can be useful if an
142     * application needs full RGBA 8888 support for instance but will
143     * still draw every pixel opaque.
144     * @hide
145     */
146    public static final int OPAQUE = 0x00000400;
147
148    /**
149     * Surface creation flag: Application requires a hardware-protected path to an
150     * external display sink. If a hardware-protected path is not available,
151     * then this surface will not be displayed on the external sink.
152     * @hide
153     */
154    public static final int PROTECTED_APP = 0x00000800;
155
156    // 0x1000 is reserved for an independent DRM protected flag in framework
157
158    /**
159     * Surface creation flag: Creates a normal surface.
160     * This is the default.
161     * @hide
162     */
163    public static final int FX_SURFACE_NORMAL   = 0x00000000;
164
165    /**
166     * Surface creation flag: Creates a Blur surface.
167     * Everything behind this surface is blurred by some amount.
168     * The quality and refresh speed of the blur effect is not settable or guaranteed.
169     * It is an error to lock a Blur surface, since it doesn't have a backing store.
170     * @hide
171     * @deprecated
172     */
173    @Deprecated
174    public static final int FX_SURFACE_BLUR = 0x00010000;
175
176    /**
177     * Surface creation flag: Creates a Dim surface.
178     * Everything behind this surface is dimmed by the amount specified
179     * in {@link #setAlpha}.  It is an error to lock a Dim surface, since it
180     * doesn't have a backing store.
181     * @hide
182     */
183    public static final int FX_SURFACE_DIM = 0x00020000;
184
185    /**
186     * @hide
187     */
188    public static final int FX_SURFACE_SCREENSHOT = 0x00030000;
189
190    /**
191     * Mask used for FX values above.
192     * @hide
193     */
194    public static final int FX_SURFACE_MASK = 0x000F0000;
195
196    /* flags used with setFlags() (keep in sync with ISurfaceComposer.h) */
197
198    /**
199     * Surface flag: Hide the surface.
200     * Equivalent to calling hide().
201     * @hide
202     */
203    public static final int SURFACE_HIDDEN = 0x01;
204
205
206    private final CloseGuard mCloseGuard = CloseGuard.get();
207    private String mName;
208
209    // Note: These fields are accessed by native code.
210    // The mSurfaceControl will only be present for Surfaces used by the window
211    // server or system processes. When this class is parceled we defer to the
212    // mSurfaceControl to do the parceling. Otherwise we parcel the
213    // mNativeSurface.
214    private int mNativeSurface; // Surface*
215    private int mNativeSurfaceControl; // SurfaceControl*
216    private int mGenerationId; // incremented each time mNativeSurface changes
217    private final Canvas mCanvas = new CompatibleCanvas();
218    private int mCanvasSaveCount; // Canvas save count at time of lockCanvas()
219
220    // The Translator for density compatibility mode.  This is used for scaling
221    // the canvas to perform the appropriate density transformation.
222    private Translator mCompatibilityTranslator;
223
224    // A matrix to scale the matrix set by application. This is set to null for
225    // non compatibility mode.
226    private Matrix mCompatibleMatrix;
227
228    private native void nativeCreate(SurfaceSession session, String name,
229            int w, int h, int format, int flags)
230            throws OutOfResourcesException;
231    private native void nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
232            throws OutOfResourcesException;
233    private native void nativeRelease();
234    private native void nativeDestroy();
235
236    private native boolean nativeIsValid();
237    private native int nativeGetIdentity();
238    private native boolean nativeIsConsumerRunningBehind();
239
240    private native Canvas nativeLockCanvas(Rect dirty);
241    private native void nativeUnlockCanvasAndPost(Canvas canvas);
242
243    private static native Bitmap nativeScreenshot(IBinder displayToken,
244            int width, int height, int minLayer, int maxLayer, boolean allLayers);
245
246    private static native void nativeOpenTransaction();
247    private static native void nativeCloseTransaction();
248    private static native void nativeSetAnimationTransaction();
249
250    private native void nativeSetLayer(int zorder);
251    private native void nativeSetPosition(float x, float y);
252    private native void nativeSetSize(int w, int h);
253    private native void nativeSetTransparentRegionHint(Region region);
254    private native void nativeSetAlpha(float alpha);
255    private native void nativeSetMatrix(float dsdx, float dtdx, float dsdy, float dtdy);
256    private native void nativeSetFlags(int flags, int mask);
257    private native void nativeSetWindowCrop(Rect crop);
258    private native void nativeSetLayerStack(int layerStack);
259
260    private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId);
261    private static native IBinder nativeCreateDisplay(String name, boolean secure);
262    private static native void nativeSetDisplaySurface(
263            IBinder displayToken, Surface surface);
264    private static native void nativeSetDisplayLayerStack(
265            IBinder displayToken, int layerStack);
266    private static native void nativeSetDisplayProjection(
267            IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect);
268    private static native boolean nativeGetDisplayInfo(
269            IBinder displayToken, PhysicalDisplayInfo outInfo);
270    private static native void nativeBlankDisplay(IBinder displayToken);
271    private static native void nativeUnblankDisplay(IBinder displayToken);
272
273    private native void nativeCopyFrom(Surface other);
274    private native void nativeTransferFrom(Surface other);
275    private native void nativeReadFromParcel(Parcel source);
276    private native void nativeWriteToParcel(Parcel dest);
277
278
279    /**
280     * Create an empty surface, which will later be filled in by readFromParcel().
281     * @hide
282     */
283    public Surface() {
284        checkHeadless();
285
286        mCloseGuard.open("release");
287    }
288
289    /**
290     * Create a surface with a name.
291     *
292     * The surface creation flags specify what kind of surface to create and
293     * certain options such as whether the surface can be assumed to be opaque
294     * and whether it should be initially hidden.  Surfaces should always be
295     * created with the {@link #HIDDEN} flag set to ensure that they are not
296     * made visible prematurely before all of the surface's properties have been
297     * configured.
298     *
299     * Good practice is to first create the surface with the {@link #HIDDEN} flag
300     * specified, open a transaction, set the surface layer, layer stack, alpha,
301     * and position, call {@link #show} if appropriate, and close the transaction.
302     *
303     * @param session The surface session, must not be null.
304     * @param name The surface name, must not be null.
305     * @param w The surface initial width.
306     * @param h The surface initial height.
307     * @param flags The surface creation flags.  Should always include {@link #HIDDEN}
308     * in the creation flags.
309     * @hide
310     */
311    public Surface(SurfaceSession session,
312            String name, int w, int h, int format, int flags)
313            throws OutOfResourcesException {
314        if (session == null) {
315            throw new IllegalArgumentException("session must not be null");
316        }
317        if (name == null) {
318            throw new IllegalArgumentException("name must not be null");
319        }
320
321        if ((flags & HIDDEN) == 0) {
322            Log.w(TAG, "Surfaces should always be created with the HIDDEN flag set "
323                    + "to ensure that they are not made visible prematurely before "
324                    + "all of the surface's properties have been configured.  "
325                    + "Set the other properties and make the surface visible within "
326                    + "a transaction.  New surface name: " + name,
327                    new Throwable());
328        }
329
330        checkHeadless();
331
332        mName = name;
333        nativeCreate(session, name, w, h, format, flags);
334
335        mCloseGuard.open("release");
336    }
337
338    /**
339     * Create Surface from a {@link SurfaceTexture}.
340     *
341     * Images drawn to the Surface will be made available to the {@link
342     * SurfaceTexture}, which can attach them to an OpenGL ES texture via {@link
343     * SurfaceTexture#updateTexImage}.
344     *
345     * @param surfaceTexture The {@link SurfaceTexture} that is updated by this
346     * Surface.
347     */
348    public Surface(SurfaceTexture surfaceTexture) {
349        if (surfaceTexture == null) {
350            throw new IllegalArgumentException("surfaceTexture must not be null");
351        }
352
353        checkHeadless();
354
355        mName = surfaceTexture.toString();
356        try {
357            nativeCreateFromSurfaceTexture(surfaceTexture);
358        } catch (OutOfResourcesException ex) {
359            // We can't throw OutOfResourcesException because it would be an API change.
360            throw new RuntimeException(ex);
361        }
362
363        mCloseGuard.open("release");
364    }
365
366    @Override
367    protected void finalize() throws Throwable {
368        try {
369            if (mCloseGuard != null) {
370                mCloseGuard.warnIfOpen();
371            }
372            nativeRelease();
373        } finally {
374            super.finalize();
375        }
376    }
377
378    /**
379     * Release the local reference to the server-side surface.
380     * Always call release() when you're done with a Surface.
381     * This will make the surface invalid.
382     */
383    public void release() {
384        nativeRelease();
385        mCloseGuard.close();
386    }
387
388    /**
389     * Free all server-side state associated with this surface and
390     * release this object's reference.  This method can only be
391     * called from the process that created the service.
392     * @hide
393     */
394    public void destroy() {
395        nativeDestroy();
396        mCloseGuard.close();
397    }
398
399    /**
400     * Returns true if this object holds a valid surface.
401     *
402     * @return True if it holds a physical surface, so lockCanvas() will succeed.
403     * Otherwise returns false.
404     */
405    public boolean isValid() {
406        return nativeIsValid();
407    }
408
409    /**
410     * Gets the generation number of this surface, incremented each time
411     * the native surface contained within this object changes.
412     *
413     * @return The current generation number.
414     * @hide
415     */
416    public int getGenerationId() {
417        return mGenerationId;
418    }
419
420    /**
421     * Returns true if the consumer of this Surface is running behind the producer.
422     *
423     * @return True if the consumer is more than one buffer ahead of the producer.
424     * @hide
425     */
426    public boolean isConsumerRunningBehind() {
427        return nativeIsConsumerRunningBehind();
428    }
429
430    /**
431     * Gets a {@link Canvas} for drawing into this surface.
432     *
433     * After drawing into the provided {@link Canvas}, the caller should
434     * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
435     *
436     * @param dirty A rectangle that represents the dirty region that the caller wants
437     * to redraw.  This function may choose to expand the dirty rectangle if for example
438     * the surface has been resized or if the previous contents of the surface were
439     * not available.  The caller should redraw the entire dirty region as represented
440     * by the contents of the dirty rect upon return from this function.
441     * The caller may also pass <code>null</code> instead, in the case where the
442     * entire surface should be redrawn.
443     * @return A canvas for drawing into the surface.
444     */
445    public Canvas lockCanvas(Rect dirty)
446            throws OutOfResourcesException, IllegalArgumentException {
447        return nativeLockCanvas(dirty);
448    }
449
450    /**
451     * Posts the new contents of the {@link Canvas} to the surface and
452     * releases the {@link Canvas}.
453     *
454     * @param canvas The canvas previously obtained from {@link #lockCanvas}.
455     */
456    public void unlockCanvasAndPost(Canvas canvas) {
457        nativeUnlockCanvasAndPost(canvas);
458    }
459
460    /**
461     * @deprecated This API has been removed and is not supported.  Do not use.
462     */
463    @Deprecated
464    public void unlockCanvas(Canvas canvas) {
465        throw new UnsupportedOperationException();
466    }
467
468    /**
469     * Sets the translator used to scale canvas's width/height in compatibility
470     * mode.
471     */
472    void setCompatibilityTranslator(Translator translator) {
473        if (translator != null) {
474            float appScale = translator.applicationScale;
475            mCompatibleMatrix = new Matrix();
476            mCompatibleMatrix.setScale(appScale, appScale);
477        }
478    }
479
480    /**
481     * Like {@link #screenshot(int, int, int, int)} but includes all
482     * Surfaces in the screenshot.
483     *
484     * @hide
485     */
486    public static Bitmap screenshot(int width, int height) {
487        // TODO: should take the display as a parameter
488        IBinder displayToken = getBuiltInDisplay(BUILT_IN_DISPLAY_ID_MAIN);
489        return nativeScreenshot(displayToken, width, height, 0, 0, true);
490    }
491
492    /**
493     * Copy the current screen contents into a bitmap and return it.
494     *
495     * @param width The desired width of the returned bitmap; the raw
496     * screen will be scaled down to this size.
497     * @param height The desired height of the returned bitmap; the raw
498     * screen will be scaled down to this size.
499     * @param minLayer The lowest (bottom-most Z order) surface layer to
500     * include in the screenshot.
501     * @param maxLayer The highest (top-most Z order) surface layer to
502     * include in the screenshot.
503     * @return Returns a Bitmap containing the screen contents, or null
504     * if an error occurs.
505     *
506     * @hide
507     */
508    public static Bitmap screenshot(int width, int height, int minLayer, int maxLayer) {
509        // TODO: should take the display as a parameter
510        IBinder displayToken = getBuiltInDisplay(BUILT_IN_DISPLAY_ID_MAIN);
511        return nativeScreenshot(displayToken, width, height, minLayer, maxLayer, false);
512    }
513
514    /*
515     * set surface parameters.
516     * needs to be inside open/closeTransaction block
517     */
518
519    /** start a transaction @hide */
520    public static void openTransaction() {
521        nativeOpenTransaction();
522    }
523
524    /** end a transaction @hide */
525    public static void closeTransaction() {
526        nativeCloseTransaction();
527    }
528
529    /** flag the transaction as an animation @hide */
530    public static void setAnimationTransaction() {
531        nativeSetAnimationTransaction();
532    }
533
534    /** @hide */
535    public void setLayer(int zorder) {
536        nativeSetLayer(zorder);
537    }
538
539    /** @hide */
540    public void setPosition(int x, int y) {
541        nativeSetPosition((float)x, (float)y);
542    }
543
544    /** @hide */
545    public void setPosition(float x, float y) {
546        nativeSetPosition(x, y);
547    }
548
549    /** @hide */
550    public void setSize(int w, int h) {
551        nativeSetSize(w, h);
552    }
553
554    /** @hide */
555    public void hide() {
556        nativeSetFlags(SURFACE_HIDDEN, SURFACE_HIDDEN);
557    }
558
559    /** @hide */
560    public void show() {
561        nativeSetFlags(0, SURFACE_HIDDEN);
562    }
563
564    /** @hide */
565    public void setTransparentRegionHint(Region region) {
566        nativeSetTransparentRegionHint(region);
567    }
568
569    /** @hide */
570    public void setAlpha(float alpha) {
571        nativeSetAlpha(alpha);
572    }
573
574    /** @hide */
575    public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
576        nativeSetMatrix(dsdx, dtdx, dsdy, dtdy);
577    }
578
579    /** @hide */
580    public void setFlags(int flags, int mask) {
581        nativeSetFlags(flags, mask);
582    }
583
584    /** @hide */
585    public void setWindowCrop(Rect crop) {
586        nativeSetWindowCrop(crop);
587    }
588
589    /** @hide */
590    public void setLayerStack(int layerStack) {
591        nativeSetLayerStack(layerStack);
592    }
593
594    /** @hide */
595    public static IBinder getBuiltInDisplay(int builtInDisplayId) {
596        return nativeGetBuiltInDisplay(builtInDisplayId);
597    }
598
599    /** @hide */
600    public static IBinder createDisplay(String name, boolean secure) {
601        if (name == null) {
602            throw new IllegalArgumentException("name must not be null");
603        }
604        return nativeCreateDisplay(name, secure);
605    }
606
607    /** @hide */
608    public static void setDisplaySurface(IBinder displayToken, Surface surface) {
609        if (displayToken == null) {
610            throw new IllegalArgumentException("displayToken must not be null");
611        }
612        nativeSetDisplaySurface(displayToken, surface);
613    }
614
615    /** @hide */
616    public static void setDisplayLayerStack(IBinder displayToken, int layerStack) {
617        if (displayToken == null) {
618            throw new IllegalArgumentException("displayToken must not be null");
619        }
620        nativeSetDisplayLayerStack(displayToken, layerStack);
621    }
622
623    /** @hide */
624    public static void setDisplayProjection(IBinder displayToken,
625            int orientation, Rect layerStackRect, Rect displayRect) {
626        if (displayToken == null) {
627            throw new IllegalArgumentException("displayToken must not be null");
628        }
629        if (layerStackRect == null) {
630            throw new IllegalArgumentException("layerStackRect must not be null");
631        }
632        if (displayRect == null) {
633            throw new IllegalArgumentException("displayRect must not be null");
634        }
635        nativeSetDisplayProjection(displayToken, orientation, layerStackRect, displayRect);
636    }
637
638    /** @hide */
639    public static boolean getDisplayInfo(IBinder displayToken, PhysicalDisplayInfo outInfo) {
640        if (displayToken == null) {
641            throw new IllegalArgumentException("displayToken must not be null");
642        }
643        if (outInfo == null) {
644            throw new IllegalArgumentException("outInfo must not be null");
645        }
646        return nativeGetDisplayInfo(displayToken, outInfo);
647    }
648
649    /** @hide */
650    public static void blankDisplay(IBinder displayToken) {
651        if (displayToken == null) {
652            throw new IllegalArgumentException("displayToken must not be null");
653        }
654        nativeBlankDisplay(displayToken);
655    }
656
657    /** @hide */
658    public static void unblankDisplay(IBinder displayToken) {
659        if (displayToken == null) {
660            throw new IllegalArgumentException("displayToken must not be null");
661        }
662        nativeUnblankDisplay(displayToken);
663    }
664
665    /**
666     * Copy another surface to this one.  This surface now holds a reference
667     * to the same data as the original surface, and is -not- the owner.
668     * This is for use by the window manager when returning a window surface
669     * back from a client, converting it from the representation being managed
670     * by the window manager to the representation the client uses to draw
671     * in to it.
672     * @hide
673     */
674    public void copyFrom(Surface other) {
675        if (other == null) {
676            throw new IllegalArgumentException("other must not be null");
677        }
678        if (other != this) {
679            nativeCopyFrom(other);
680        }
681    }
682
683    /**
684     * Transfer the native state from 'other' to this surface, releasing it
685     * from 'other'.  This is for use in the client side for drawing into a
686     * surface; not guaranteed to work on the window manager side.
687     * This is for use by the client to move the underlying surface from
688     * one Surface object to another, in particular in SurfaceFlinger.
689     * @hide.
690     */
691    public void transferFrom(Surface other) {
692        if (other == null) {
693            throw new IllegalArgumentException("other must not be null");
694        }
695        if (other != this) {
696            nativeTransferFrom(other);
697        }
698    }
699
700    @Override
701    public int describeContents() {
702        return 0;
703    }
704
705    public void readFromParcel(Parcel source) {
706        if (source == null) {
707            throw new IllegalArgumentException("source must not be null");
708        }
709
710        mName = source.readString();
711        nativeReadFromParcel(source);
712    }
713
714    @Override
715    public void writeToParcel(Parcel dest, int flags) {
716        if (dest == null) {
717            throw new IllegalArgumentException("dest must not be null");
718        }
719
720        dest.writeString(mName);
721        nativeWriteToParcel(dest);
722        if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
723            release();
724        }
725    }
726
727    @Override
728    public String toString() {
729        return "Surface(name=" + mName + ", identity=" + nativeGetIdentity() + ")";
730    }
731
732    private static void checkHeadless() {
733        if (HEADLESS) {
734            throw new UnsupportedOperationException("Device is headless");
735        }
736    }
737
738    /**
739     * Exception thrown when a surface couldn't be created or resized.
740     */
741    public static class OutOfResourcesException extends Exception {
742        public OutOfResourcesException() {
743        }
744
745        public OutOfResourcesException(String name) {
746            super(name);
747        }
748    }
749
750    /**
751     * Describes the properties of a physical display known to surface flinger.
752     * @hide
753     */
754    public static final class PhysicalDisplayInfo {
755        public int width;
756        public int height;
757        public float refreshRate;
758        public float density;
759        public float xDpi;
760        public float yDpi;
761        public boolean secure;
762
763        public PhysicalDisplayInfo() {
764        }
765
766        public PhysicalDisplayInfo(PhysicalDisplayInfo other) {
767            copyFrom(other);
768        }
769
770        @Override
771        public boolean equals(Object o) {
772            return o instanceof PhysicalDisplayInfo && equals((PhysicalDisplayInfo)o);
773        }
774
775        public boolean equals(PhysicalDisplayInfo other) {
776            return other != null
777                    && width == other.width
778                    && height == other.height
779                    && refreshRate == other.refreshRate
780                    && density == other.density
781                    && xDpi == other.xDpi
782                    && yDpi == other.yDpi
783                    && secure == other.secure;
784        }
785
786        @Override
787        public int hashCode() {
788            return 0; // don't care
789        }
790
791        public void copyFrom(PhysicalDisplayInfo other) {
792            width = other.width;
793            height = other.height;
794            refreshRate = other.refreshRate;
795            density = other.density;
796            xDpi = other.xDpi;
797            yDpi = other.yDpi;
798            secure = other.secure;
799        }
800
801        // For debugging purposes
802        @Override
803        public String toString() {
804            return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, "
805                    + "density " + density + ", " + xDpi + " x " + yDpi + " dpi, secure " + secure
806                    + "}";
807        }
808    }
809
810    /**
811     * A Canvas class that can handle the compatibility mode.
812     * This does two things differently.
813     * <ul>
814     * <li>Returns the width and height of the target metrics, rather than
815     * native. For example, the canvas returns 320x480 even if an app is running
816     * in WVGA high density.
817     * <li>Scales the matrix in setMatrix by the application scale, except if
818     * the matrix looks like obtained from getMatrix. This is a hack to handle
819     * the case that an application uses getMatrix to keep the original matrix,
820     * set matrix of its own, then set the original matrix back. There is no
821     * perfect solution that works for all cases, and there are a lot of cases
822     * that this model does not work, but we hope this works for many apps.
823     * </ul>
824     */
825    private final class CompatibleCanvas extends Canvas {
826        // A temp matrix to remember what an application obtained via {@link getMatrix}
827        private Matrix mOrigMatrix = null;
828
829        @Override
830        public int getWidth() {
831            int w = super.getWidth();
832            if (mCompatibilityTranslator != null) {
833                w = (int)(w * mCompatibilityTranslator.applicationInvertedScale + .5f);
834            }
835            return w;
836        }
837
838        @Override
839        public int getHeight() {
840            int h = super.getHeight();
841            if (mCompatibilityTranslator != null) {
842                h = (int)(h * mCompatibilityTranslator.applicationInvertedScale + .5f);
843            }
844            return h;
845        }
846
847        @Override
848        public void setMatrix(Matrix matrix) {
849            if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) {
850                // don't scale the matrix if it's not compatibility mode, or
851                // the matrix was obtained from getMatrix.
852                super.setMatrix(matrix);
853            } else {
854                Matrix m = new Matrix(mCompatibleMatrix);
855                m.preConcat(matrix);
856                super.setMatrix(m);
857            }
858        }
859
860        @Override
861        public void getMatrix(Matrix m) {
862            super.getMatrix(m);
863            if (mOrigMatrix == null) {
864                mOrigMatrix = new Matrix();
865            }
866            mOrigMatrix.set(m);
867        }
868    }
869}
870