WallpaperService.java revision 526505da256a9af8542e592c566bd87334d075e6
1/*
2 * Copyright (C) 2009 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.service.wallpaper;
18
19import android.content.res.TypedArray;
20import android.graphics.Canvas;
21import android.view.WindowInsets;
22
23import com.android.internal.R;
24import com.android.internal.os.HandlerCaller;
25import com.android.internal.util.ScreenShapeHelper;
26import com.android.internal.view.BaseIWindow;
27import com.android.internal.view.BaseSurfaceHolder;
28
29import android.annotation.SdkConstant;
30import android.annotation.SdkConstant.SdkConstantType;
31import android.app.Service;
32import android.app.WallpaperManager;
33import android.content.Context;
34import android.content.Intent;
35import android.content.res.Configuration;
36import android.graphics.PixelFormat;
37import android.graphics.Rect;
38import android.hardware.display.DisplayManager;
39import android.hardware.display.DisplayManager.DisplayListener;
40import android.os.Bundle;
41import android.os.IBinder;
42import android.os.Looper;
43import android.os.Message;
44import android.os.RemoteException;
45import android.util.Log;
46import android.view.Display;
47import android.view.Gravity;
48import android.view.IWindowSession;
49import android.view.InputChannel;
50import android.view.InputDevice;
51import android.view.InputEvent;
52import android.view.InputEventReceiver;
53import android.view.MotionEvent;
54import android.view.SurfaceHolder;
55import android.view.View;
56import android.view.ViewGroup;
57import android.view.WindowManager;
58import android.view.WindowManager.LayoutParams;
59import android.view.WindowManagerGlobal;
60
61import java.io.FileDescriptor;
62import java.io.PrintWriter;
63import java.util.ArrayList;
64
65/**
66 * A wallpaper service is responsible for showing a live wallpaper behind
67 * applications that would like to sit on top of it.  This service object
68 * itself does very little -- its only purpose is to generate instances of
69 * {@link Engine} as needed.  Implementing a wallpaper thus
70 * involves subclassing from this, subclassing an Engine implementation,
71 * and implementing {@link #onCreateEngine()} to return a new instance of
72 * your engine.
73 */
74public abstract class WallpaperService extends Service {
75    /**
76     * The {@link Intent} that must be declared as handled by the service.
77     * To be supported, the service must also require the
78     * {@link android.Manifest.permission#BIND_WALLPAPER} permission so
79     * that other applications can not abuse it.
80     */
81    @SdkConstant(SdkConstantType.SERVICE_ACTION)
82    public static final String SERVICE_INTERFACE =
83            "android.service.wallpaper.WallpaperService";
84
85    /**
86     * Name under which a WallpaperService component publishes information
87     * about itself.  This meta-data must reference an XML resource containing
88     * a <code>&lt;{@link android.R.styleable#Wallpaper wallpaper}&gt;</code>
89     * tag.
90     */
91    public static final String SERVICE_META_DATA = "android.service.wallpaper";
92
93    static final String TAG = "WallpaperService";
94    static final boolean DEBUG = false;
95
96    private static final int DO_ATTACH = 10;
97    private static final int DO_DETACH = 20;
98    private static final int DO_SET_DESIRED_SIZE = 30;
99    private static final int DO_SET_DISPLAY_PADDING = 40;
100
101    private static final int MSG_UPDATE_SURFACE = 10000;
102    private static final int MSG_VISIBILITY_CHANGED = 10010;
103    private static final int MSG_WALLPAPER_OFFSETS = 10020;
104    private static final int MSG_WALLPAPER_COMMAND = 10025;
105    private static final int MSG_WINDOW_RESIZED = 10030;
106    private static final int MSG_WINDOW_MOVED = 10035;
107    private static final int MSG_TOUCH_EVENT = 10040;
108
109    private final ArrayList<Engine> mActiveEngines
110            = new ArrayList<Engine>();
111
112    static final class WallpaperCommand {
113        String action;
114        int x;
115        int y;
116        int z;
117        Bundle extras;
118        boolean sync;
119    }
120
121    /**
122     * The actual implementation of a wallpaper.  A wallpaper service may
123     * have multiple instances running (for example as a real wallpaper
124     * and as a preview), each of which is represented by its own Engine
125     * instance.  You must implement {@link WallpaperService#onCreateEngine()}
126     * to return your concrete Engine implementation.
127     */
128    public class Engine {
129        IWallpaperEngineWrapper mIWallpaperEngine;
130
131        // Copies from mIWallpaperEngine.
132        HandlerCaller mCaller;
133        IWallpaperConnection mConnection;
134        IBinder mWindowToken;
135
136        boolean mInitializing = true;
137        boolean mVisible;
138        boolean mReportedVisible;
139        boolean mDestroyed;
140
141        // Current window state.
142        boolean mCreated;
143        boolean mSurfaceCreated;
144        boolean mIsCreating;
145        boolean mDrawingAllowed;
146        boolean mOffsetsChanged;
147        boolean mFixedSizeAllowed;
148        int mWidth;
149        int mHeight;
150        int mFormat;
151        int mType;
152        int mCurWidth;
153        int mCurHeight;
154        int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
155        int mWindowPrivateFlags =
156                WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
157        int mCurWindowFlags = mWindowFlags;
158        int mCurWindowPrivateFlags = mWindowPrivateFlags;
159        final Rect mVisibleInsets = new Rect();
160        final Rect mWinFrame = new Rect();
161        final Rect mOverscanInsets = new Rect();
162        final Rect mContentInsets = new Rect();
163        final Rect mStableInsets = new Rect();
164        final Rect mOutsets = new Rect();
165        final Rect mDispatchedOverscanInsets = new Rect();
166        final Rect mDispatchedContentInsets = new Rect();
167        final Rect mDispatchedStableInsets = new Rect();
168        final Rect mDispatchedOutsets = new Rect();
169        final Rect mFinalSystemInsets = new Rect();
170        final Rect mFinalStableInsets = new Rect();
171        final Rect mBackdropFrame = new Rect();
172        final Configuration mConfiguration = new Configuration();
173
174        final WindowManager.LayoutParams mLayout
175                = new WindowManager.LayoutParams();
176        IWindowSession mSession;
177        InputChannel mInputChannel;
178
179        final Object mLock = new Object();
180        boolean mOffsetMessageEnqueued;
181        float mPendingXOffset;
182        float mPendingYOffset;
183        float mPendingXOffsetStep;
184        float mPendingYOffsetStep;
185        boolean mPendingSync;
186        MotionEvent mPendingMove;
187
188        DisplayManager mDisplayManager;
189        Display mDisplay;
190        private int mDisplayState;
191
192        final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
193            {
194                mRequestedFormat = PixelFormat.RGBX_8888;
195            }
196
197            @Override
198            public boolean onAllowLockCanvas() {
199                return mDrawingAllowed;
200            }
201
202            @Override
203            public void onRelayoutContainer() {
204                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
205                mCaller.sendMessage(msg);
206            }
207
208            @Override
209            public void onUpdateSurface() {
210                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
211                mCaller.sendMessage(msg);
212            }
213
214            public boolean isCreating() {
215                return mIsCreating;
216            }
217
218            @Override
219            public void setFixedSize(int width, int height) {
220                if (!mFixedSizeAllowed) {
221                    // Regular apps can't do this.  It can only work for
222                    // certain designs of window animations, so you can't
223                    // rely on it.
224                    throw new UnsupportedOperationException(
225                            "Wallpapers currently only support sizing from layout");
226                }
227                super.setFixedSize(width, height);
228            }
229
230            public void setKeepScreenOn(boolean screenOn) {
231                throw new UnsupportedOperationException(
232                        "Wallpapers do not support keep screen on");
233            }
234
235            @Override
236            public Canvas lockCanvas() {
237                if (mDisplayState == Display.STATE_DOZE
238                        || mDisplayState == Display.STATE_DOZE_SUSPEND) {
239                    try {
240                        mSession.pokeDrawLock(mWindow);
241                    } catch (RemoteException e) {
242                        // System server died, can be ignored.
243                    }
244                }
245                return super.lockCanvas();
246            }
247        };
248
249        final class WallpaperInputEventReceiver extends InputEventReceiver {
250            public WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper) {
251                super(inputChannel, looper);
252            }
253
254            @Override
255            public void onInputEvent(InputEvent event) {
256                boolean handled = false;
257                try {
258                    if (event instanceof MotionEvent
259                            && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
260                        MotionEvent dup = MotionEvent.obtainNoHistory((MotionEvent)event);
261                        dispatchPointer(dup);
262                        handled = true;
263                    }
264                } finally {
265                    finishInputEvent(event, handled);
266                }
267            }
268        }
269        WallpaperInputEventReceiver mInputEventReceiver;
270
271        final BaseIWindow mWindow = new BaseIWindow() {
272            @Override
273            public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
274                    Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
275                    Configuration newConfig, Rect backDropRect, boolean forceLayout,
276                    boolean alwaysConsumeNavBar) {
277                Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
278                        reportDraw ? 1 : 0, outsets);
279                mCaller.sendMessage(msg);
280            }
281
282            @Override
283            public void moved(int newX, int newY) {
284                Message msg = mCaller.obtainMessageII(MSG_WINDOW_MOVED, newX, newY);
285                mCaller.sendMessage(msg);
286            }
287
288            @Override
289            public void dispatchAppVisibility(boolean visible) {
290                // We don't do this in preview mode; we'll let the preview
291                // activity tell us when to run.
292                if (!mIWallpaperEngine.mIsPreview) {
293                    Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
294                            visible ? 1 : 0);
295                    mCaller.sendMessage(msg);
296                }
297            }
298
299            @Override
300            public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
301                    boolean sync) {
302                synchronized (mLock) {
303                    if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y);
304                    mPendingXOffset = x;
305                    mPendingYOffset = y;
306                    mPendingXOffsetStep = xStep;
307                    mPendingYOffsetStep = yStep;
308                    if (sync) {
309                        mPendingSync = true;
310                    }
311                    if (!mOffsetMessageEnqueued) {
312                        mOffsetMessageEnqueued = true;
313                        Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS);
314                        mCaller.sendMessage(msg);
315                    }
316                }
317            }
318
319            @Override
320            public void dispatchWallpaperCommand(String action, int x, int y,
321                    int z, Bundle extras, boolean sync) {
322                synchronized (mLock) {
323                    if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y);
324                    WallpaperCommand cmd = new WallpaperCommand();
325                    cmd.action = action;
326                    cmd.x = x;
327                    cmd.y = y;
328                    cmd.z = z;
329                    cmd.extras = extras;
330                    cmd.sync = sync;
331                    Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND);
332                    msg.obj = cmd;
333                    mCaller.sendMessage(msg);
334                }
335            }
336        };
337
338        /**
339         * Provides access to the surface in which this wallpaper is drawn.
340         */
341        public SurfaceHolder getSurfaceHolder() {
342            return mSurfaceHolder;
343        }
344
345        /**
346         * Convenience for {@link WallpaperManager#getDesiredMinimumWidth()
347         * WallpaperManager.getDesiredMinimumWidth()}, returning the width
348         * that the system would like this wallpaper to run in.
349         */
350        public int getDesiredMinimumWidth() {
351            return mIWallpaperEngine.mReqWidth;
352        }
353
354        /**
355         * Convenience for {@link WallpaperManager#getDesiredMinimumHeight()
356         * WallpaperManager.getDesiredMinimumHeight()}, returning the height
357         * that the system would like this wallpaper to run in.
358         */
359        public int getDesiredMinimumHeight() {
360            return mIWallpaperEngine.mReqHeight;
361        }
362
363        /**
364         * Return whether the wallpaper is currently visible to the user,
365         * this is the last value supplied to
366         * {@link #onVisibilityChanged(boolean)}.
367         */
368        public boolean isVisible() {
369            return mReportedVisible;
370        }
371
372        /**
373         * Returns true if this engine is running in preview mode -- that is,
374         * it is being shown to the user before they select it as the actual
375         * wallpaper.
376         */
377        public boolean isPreview() {
378            return mIWallpaperEngine.mIsPreview;
379        }
380
381        /**
382         * Control whether this wallpaper will receive raw touch events
383         * from the window manager as the user interacts with the window
384         * that is currently displaying the wallpaper.  By default they
385         * are turned off.  If enabled, the events will be received in
386         * {@link #onTouchEvent(MotionEvent)}.
387         */
388        public void setTouchEventsEnabled(boolean enabled) {
389            mWindowFlags = enabled
390                    ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
391                    : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
392            if (mCreated) {
393                updateSurface(false, false, false);
394            }
395        }
396
397        /**
398         * Control whether this wallpaper will receive notifications when the wallpaper
399         * has been scrolled. By default, wallpapers will receive notifications, although
400         * the default static image wallpapers do not. It is a performance optimization to
401         * set this to false.
402         *
403         * @param enabled whether the wallpaper wants to receive offset notifications
404         */
405        public void setOffsetNotificationsEnabled(boolean enabled) {
406            mWindowPrivateFlags = enabled
407                    ? (mWindowPrivateFlags |
408                        WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS)
409                    : (mWindowPrivateFlags &
410                        ~WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS);
411            if (mCreated) {
412                updateSurface(false, false, false);
413            }
414        }
415
416        /** {@hide} */
417        public void setFixedSizeAllowed(boolean allowed) {
418            mFixedSizeAllowed = allowed;
419        }
420
421        /**
422         * Called once to initialize the engine.  After returning, the
423         * engine's surface will be created by the framework.
424         */
425        public void onCreate(SurfaceHolder surfaceHolder) {
426        }
427
428        /**
429         * Called right before the engine is going away.  After this the
430         * surface will be destroyed and this Engine object is no longer
431         * valid.
432         */
433        public void onDestroy() {
434        }
435
436        /**
437         * Called to inform you of the wallpaper becoming visible or
438         * hidden.  <em>It is very important that a wallpaper only use
439         * CPU while it is visible.</em>.
440         */
441        public void onVisibilityChanged(boolean visible) {
442        }
443
444        /**
445         * Called with the current insets that are in effect for the wallpaper.
446         * This gives you the part of the overall wallpaper surface that will
447         * generally be visible to the user (ignoring position offsets applied to it).
448         *
449         * @param insets Insets to apply.
450         */
451        public void onApplyWindowInsets(WindowInsets insets) {
452        }
453
454        /**
455         * Called as the user performs touch-screen interaction with the
456         * window that is currently showing this wallpaper.  Note that the
457         * events you receive here are driven by the actual application the
458         * user is interacting with, so if it is slow you will get fewer
459         * move events.
460         */
461        public void onTouchEvent(MotionEvent event) {
462        }
463
464        /**
465         * Called to inform you of the wallpaper's offsets changing
466         * within its contain, corresponding to the container's
467         * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float)
468         * WallpaperManager.setWallpaperOffsets()}.
469         */
470        public void onOffsetsChanged(float xOffset, float yOffset,
471                float xOffsetStep, float yOffsetStep,
472                int xPixelOffset, int yPixelOffset) {
473        }
474
475        /**
476         * Process a command that was sent to the wallpaper with
477         * {@link WallpaperManager#sendWallpaperCommand}.
478         * The default implementation does nothing, and always returns null
479         * as the result.
480         *
481         * @param action The name of the command to perform.  This tells you
482         * what to do and how to interpret the rest of the arguments.
483         * @param x Generic integer parameter.
484         * @param y Generic integer parameter.
485         * @param z Generic integer parameter.
486         * @param extras Any additional parameters.
487         * @param resultRequested If true, the caller is requesting that
488         * a result, appropriate for the command, be returned back.
489         * @return If returning a result, create a Bundle and place the
490         * result data in to it.  Otherwise return null.
491         */
492        public Bundle onCommand(String action, int x, int y, int z,
493                Bundle extras, boolean resultRequested) {
494            return null;
495        }
496
497        /**
498         * Called when an application has changed the desired virtual size of
499         * the wallpaper.
500         */
501        public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
502        }
503
504        /**
505         * Convenience for {@link SurfaceHolder.Callback#surfaceChanged
506         * SurfaceHolder.Callback.surfaceChanged()}.
507         */
508        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
509        }
510
511        /**
512         * Convenience for {@link SurfaceHolder.Callback2#surfaceRedrawNeeded
513         * SurfaceHolder.Callback.surfaceRedrawNeeded()}.
514         */
515        public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
516        }
517
518        /**
519         * Convenience for {@link SurfaceHolder.Callback#surfaceCreated
520         * SurfaceHolder.Callback.surfaceCreated()}.
521         */
522        public void onSurfaceCreated(SurfaceHolder holder) {
523        }
524
525        /**
526         * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed
527         * SurfaceHolder.Callback.surfaceDestroyed()}.
528         */
529        public void onSurfaceDestroyed(SurfaceHolder holder) {
530        }
531
532        protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
533            out.print(prefix); out.print("mInitializing="); out.print(mInitializing);
534                    out.print(" mDestroyed="); out.println(mDestroyed);
535            out.print(prefix); out.print("mVisible="); out.print(mVisible);
536                    out.print(" mReportedVisible="); out.println(mReportedVisible);
537            out.print(prefix); out.print("mDisplay="); out.println(mDisplay);
538            out.print(prefix); out.print("mCreated="); out.print(mCreated);
539                    out.print(" mSurfaceCreated="); out.print(mSurfaceCreated);
540                    out.print(" mIsCreating="); out.print(mIsCreating);
541                    out.print(" mDrawingAllowed="); out.println(mDrawingAllowed);
542            out.print(prefix); out.print("mWidth="); out.print(mWidth);
543                    out.print(" mCurWidth="); out.print(mCurWidth);
544                    out.print(" mHeight="); out.print(mHeight);
545                    out.print(" mCurHeight="); out.println(mCurHeight);
546            out.print(prefix); out.print("mType="); out.print(mType);
547                    out.print(" mWindowFlags="); out.print(mWindowFlags);
548                    out.print(" mCurWindowFlags="); out.println(mCurWindowFlags);
549            out.print(prefix); out.print("mWindowPrivateFlags="); out.print(mWindowPrivateFlags);
550                    out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags);
551            out.print(prefix); out.print("mVisibleInsets=");
552                    out.print(mVisibleInsets.toShortString());
553                    out.print(" mWinFrame="); out.print(mWinFrame.toShortString());
554                    out.print(" mContentInsets="); out.println(mContentInsets.toShortString());
555            out.print(prefix); out.print("mConfiguration="); out.println(mConfiguration);
556            out.print(prefix); out.print("mLayout="); out.println(mLayout);
557            synchronized (mLock) {
558                out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
559                        out.print(" mPendingXOffset="); out.println(mPendingXOffset);
560                out.print(prefix); out.print("mPendingXOffsetStep=");
561                        out.print(mPendingXOffsetStep);
562                        out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep);
563                out.print(prefix); out.print("mOffsetMessageEnqueued=");
564                        out.print(mOffsetMessageEnqueued);
565                        out.print(" mPendingSync="); out.println(mPendingSync);
566                if (mPendingMove != null) {
567                    out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove);
568                }
569            }
570        }
571
572        private void dispatchPointer(MotionEvent event) {
573            if (event.isTouchEvent()) {
574                synchronized (mLock) {
575                    if (event.getAction() == MotionEvent.ACTION_MOVE) {
576                        mPendingMove = event;
577                    } else {
578                        mPendingMove = null;
579                    }
580                }
581                Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
582                mCaller.sendMessage(msg);
583            } else {
584                event.recycle();
585            }
586        }
587
588        void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
589            if (mDestroyed) {
590                Log.w(TAG, "Ignoring updateSurface: destroyed");
591            }
592
593            boolean fixedSize = false;
594            int myWidth = mSurfaceHolder.getRequestedWidth();
595            if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT;
596            else fixedSize = true;
597            int myHeight = mSurfaceHolder.getRequestedHeight();
598            if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT;
599            else fixedSize = true;
600
601            final boolean creating = !mCreated;
602            final boolean surfaceCreating = !mSurfaceCreated;
603            final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
604            boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
605            boolean insetsChanged = !mCreated;
606            final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
607            final boolean flagsChanged = mCurWindowFlags != mWindowFlags ||
608                    mCurWindowPrivateFlags != mWindowPrivateFlags;
609            if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged
610                    || typeChanged || flagsChanged || redrawNeeded
611                    || !mIWallpaperEngine.mShownReported) {
612
613                if (DEBUG) Log.v(TAG, "Changes: creating=" + creating
614                        + " format=" + formatChanged + " size=" + sizeChanged);
615
616                try {
617                    mWidth = myWidth;
618                    mHeight = myHeight;
619                    mFormat = mSurfaceHolder.getRequestedFormat();
620                    mType = mSurfaceHolder.getRequestedType();
621
622                    mLayout.x = 0;
623                    mLayout.y = 0;
624                    mLayout.width = myWidth;
625                    mLayout.height = myHeight;
626
627                    mLayout.format = mFormat;
628
629                    mCurWindowFlags = mWindowFlags;
630                    mLayout.flags = mWindowFlags
631                            | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
632                            | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
633                            | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
634                            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
635                    mCurWindowPrivateFlags = mWindowPrivateFlags;
636                    mLayout.privateFlags = mWindowPrivateFlags;
637
638                    mLayout.memoryType = mType;
639                    mLayout.token = mWindowToken;
640
641                    if (!mCreated) {
642                        // Retrieve watch round info
643                        TypedArray windowStyle = obtainStyledAttributes(
644                                com.android.internal.R.styleable.Window);
645                        windowStyle.recycle();
646
647                        // Add window
648                        mLayout.type = mIWallpaperEngine.mWindowType;
649                        mLayout.gravity = Gravity.START|Gravity.TOP;
650                        mLayout.setTitle(WallpaperService.this.getClass().getName());
651                        mLayout.windowAnimations =
652                                com.android.internal.R.style.Animation_Wallpaper;
653                        mInputChannel = new InputChannel();
654                        if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
655                            Display.DEFAULT_DISPLAY, mContentInsets, mStableInsets, mOutsets,
656                                mInputChannel) < 0) {
657                            Log.w(TAG, "Failed to add window while updating wallpaper surface.");
658                            return;
659                        }
660                        mCreated = true;
661
662                        mInputEventReceiver = new WallpaperInputEventReceiver(
663                                mInputChannel, Looper.myLooper());
664                    }
665
666                    mSurfaceHolder.mSurfaceLock.lock();
667                    mDrawingAllowed = true;
668
669                    if (!fixedSize) {
670                        mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding);
671                        mLayout.surfaceInsets.left += mOutsets.left;
672                        mLayout.surfaceInsets.top += mOutsets.top;
673                        mLayout.surfaceInsets.right += mOutsets.right;
674                        mLayout.surfaceInsets.bottom += mOutsets.bottom;
675                    } else {
676                        mLayout.surfaceInsets.set(0, 0, 0, 0);
677                    }
678                    final int relayoutResult = mSession.relayout(
679                        mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
680                            View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
681                            mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
682                            mConfiguration, mSurfaceHolder.mSurface);
683
684                    if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
685                            + ", frame=" + mWinFrame);
686
687                    int w = mWinFrame.width();
688                    int h = mWinFrame.height();
689
690                    if (!fixedSize) {
691                        final Rect padding = mIWallpaperEngine.mDisplayPadding;
692                        w += padding.left + padding.right + mOutsets.left + mOutsets.right;
693                        h += padding.top + padding.bottom + mOutsets.top + mOutsets.bottom;
694                        mOverscanInsets.left += padding.left;
695                        mOverscanInsets.top += padding.top;
696                        mOverscanInsets.right += padding.right;
697                        mOverscanInsets.bottom += padding.bottom;
698                        mContentInsets.left += padding.left;
699                        mContentInsets.top += padding.top;
700                        mContentInsets.right += padding.right;
701                        mContentInsets.bottom += padding.bottom;
702                        mStableInsets.left += padding.left;
703                        mStableInsets.top += padding.top;
704                        mStableInsets.right += padding.right;
705                        mStableInsets.bottom += padding.bottom;
706                    }
707
708                    if (mCurWidth != w) {
709                        sizeChanged = true;
710                        mCurWidth = w;
711                    }
712                    if (mCurHeight != h) {
713                        sizeChanged = true;
714                        mCurHeight = h;
715                    }
716
717                    if (DEBUG) {
718                        Log.v(TAG, "Wallpaper size has changed: (" + mCurWidth + ", " + mCurHeight);
719                    }
720
721                    insetsChanged |= !mDispatchedOverscanInsets.equals(mOverscanInsets);
722                    insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets);
723                    insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets);
724                    insetsChanged |= !mDispatchedOutsets.equals(mOutsets);
725
726                    mSurfaceHolder.setSurfaceFrameSize(w, h);
727                    mSurfaceHolder.mSurfaceLock.unlock();
728
729                    if (!mSurfaceHolder.mSurface.isValid()) {
730                        reportSurfaceDestroyed();
731                        if (DEBUG) Log.v(TAG, "Layout: Surface destroyed");
732                        return;
733                    }
734
735                    boolean didSurface = false;
736
737                    try {
738                        mSurfaceHolder.ungetCallbacks();
739
740                        if (surfaceCreating) {
741                            mIsCreating = true;
742                            didSurface = true;
743                            if (DEBUG) Log.v(TAG, "onSurfaceCreated("
744                                    + mSurfaceHolder + "): " + this);
745                            onSurfaceCreated(mSurfaceHolder);
746                            SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
747                            if (callbacks != null) {
748                                for (SurfaceHolder.Callback c : callbacks) {
749                                    c.surfaceCreated(mSurfaceHolder);
750                                }
751                            }
752                        }
753
754                        redrawNeeded |= creating || (relayoutResult
755                                & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
756
757                        if (forceReport || creating || surfaceCreating
758                                || formatChanged || sizeChanged) {
759                            if (DEBUG) {
760                                RuntimeException e = new RuntimeException();
761                                e.fillInStackTrace();
762                                Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating
763                                        + " formatChanged=" + formatChanged
764                                        + " sizeChanged=" + sizeChanged, e);
765                            }
766                            if (DEBUG) Log.v(TAG, "onSurfaceChanged("
767                                    + mSurfaceHolder + ", " + mFormat
768                                    + ", " + mCurWidth + ", " + mCurHeight
769                                    + "): " + this);
770                            didSurface = true;
771                            onSurfaceChanged(mSurfaceHolder, mFormat,
772                                    mCurWidth, mCurHeight);
773                            SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
774                            if (callbacks != null) {
775                                for (SurfaceHolder.Callback c : callbacks) {
776                                    c.surfaceChanged(mSurfaceHolder, mFormat,
777                                            mCurWidth, mCurHeight);
778                                }
779                            }
780                        }
781
782                        if (insetsChanged) {
783                            mDispatchedOverscanInsets.set(mOverscanInsets);
784                            mDispatchedOverscanInsets.left += mOutsets.left;
785                            mDispatchedOverscanInsets.top += mOutsets.top;
786                            mDispatchedOverscanInsets.right += mOutsets.right;
787                            mDispatchedOverscanInsets.bottom += mOutsets.bottom;
788                            mDispatchedContentInsets.set(mContentInsets);
789                            mDispatchedStableInsets.set(mStableInsets);
790                            mDispatchedOutsets.set(mOutsets);
791                            mFinalSystemInsets.set(mDispatchedOverscanInsets);
792                            mFinalStableInsets.set(mDispatchedStableInsets);
793                            WindowInsets insets = new WindowInsets(mFinalSystemInsets,
794                                    null, mFinalStableInsets,
795                                    getResources().getConfiguration().isScreenRound(), false);
796                            if (DEBUG) {
797                                Log.v(TAG, "dispatching insets=" + insets);
798                            }
799                            onApplyWindowInsets(insets);
800                        }
801
802                        if (redrawNeeded) {
803                            onSurfaceRedrawNeeded(mSurfaceHolder);
804                            SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
805                            if (callbacks != null) {
806                                for (SurfaceHolder.Callback c : callbacks) {
807                                    if (c instanceof SurfaceHolder.Callback2) {
808                                        ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
809                                                mSurfaceHolder);
810                                    }
811                                }
812                            }
813                        }
814
815                        if (didSurface && !mReportedVisible) {
816                            // This wallpaper is currently invisible, but its
817                            // surface has changed.  At this point let's tell it
818                            // again that it is invisible in case the report about
819                            // the surface caused it to start running.  We really
820                            // don't want wallpapers running when not visible.
821                            if (mIsCreating) {
822                                // Some wallpapers will ignore this call if they
823                                // had previously been told they were invisble,
824                                // so if we are creating a new surface then toggle
825                                // the state to get them to notice.
826                                if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: "
827                                        + this);
828                                onVisibilityChanged(true);
829                            }
830                            if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: "
831                                        + this);
832                            onVisibilityChanged(false);
833                        }
834
835                    } finally {
836                        mIsCreating = false;
837                        mSurfaceCreated = true;
838                        if (redrawNeeded) {
839                            mSession.finishDrawing(mWindow);
840                        }
841                        mIWallpaperEngine.reportShown();
842                    }
843                } catch (RemoteException ex) {
844                }
845                if (DEBUG) Log.v(
846                    TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
847                    " w=" + mLayout.width + " h=" + mLayout.height);
848            }
849        }
850
851        void attach(IWallpaperEngineWrapper wrapper) {
852            if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper);
853            if (mDestroyed) {
854                return;
855            }
856
857            mIWallpaperEngine = wrapper;
858            mCaller = wrapper.mCaller;
859            mConnection = wrapper.mConnection;
860            mWindowToken = wrapper.mWindowToken;
861            mSurfaceHolder.setSizeFromLayout();
862            mInitializing = true;
863            mSession = WindowManagerGlobal.getWindowSession();
864
865            mWindow.setSession(mSession);
866
867            mLayout.packageName = getPackageName();
868
869            mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE);
870            mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler());
871            mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
872            mDisplayState = mDisplay.getState();
873
874            if (DEBUG) Log.v(TAG, "onCreate(): " + this);
875            onCreate(mSurfaceHolder);
876
877            mInitializing = false;
878            mReportedVisible = false;
879            updateSurface(false, false, false);
880        }
881
882        void doDesiredSizeChanged(int desiredWidth, int desiredHeight) {
883            if (!mDestroyed) {
884                if (DEBUG) Log.v(TAG, "onDesiredSizeChanged("
885                        + desiredWidth + "," + desiredHeight + "): " + this);
886                mIWallpaperEngine.mReqWidth = desiredWidth;
887                mIWallpaperEngine.mReqHeight = desiredHeight;
888                onDesiredSizeChanged(desiredWidth, desiredHeight);
889                doOffsetsChanged(true);
890            }
891        }
892
893        void doDisplayPaddingChanged(Rect padding) {
894            if (!mDestroyed) {
895                if (DEBUG) Log.v(TAG, "onDisplayPaddingChanged(" + padding + "): " + this);
896                if (!mIWallpaperEngine.mDisplayPadding.equals(padding)) {
897                    mIWallpaperEngine.mDisplayPadding.set(padding);
898                    updateSurface(true, false, false);
899                }
900            }
901        }
902
903        void doVisibilityChanged(boolean visible) {
904            if (!mDestroyed) {
905                mVisible = visible;
906                reportVisibility();
907            }
908        }
909
910        void reportVisibility() {
911            if (!mDestroyed) {
912                mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getState();
913                boolean visible = mVisible && mDisplayState != Display.STATE_OFF;
914                if (mReportedVisible != visible) {
915                    mReportedVisible = visible;
916                    if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible
917                            + "): " + this);
918                    if (visible) {
919                        // If becoming visible, in preview mode the surface
920                        // may have been destroyed so now we need to make
921                        // sure it is re-created.
922                        doOffsetsChanged(false);
923                        updateSurface(false, false, false);
924                    }
925                    onVisibilityChanged(visible);
926                }
927            }
928        }
929
930        void doOffsetsChanged(boolean always) {
931            if (mDestroyed) {
932                return;
933            }
934
935            if (!always && !mOffsetsChanged) {
936                return;
937            }
938
939            float xOffset;
940            float yOffset;
941            float xOffsetStep;
942            float yOffsetStep;
943            boolean sync;
944            synchronized (mLock) {
945                xOffset = mPendingXOffset;
946                yOffset = mPendingYOffset;
947                xOffsetStep = mPendingXOffsetStep;
948                yOffsetStep = mPendingYOffsetStep;
949                sync = mPendingSync;
950                mPendingSync = false;
951                mOffsetMessageEnqueued = false;
952            }
953
954            if (mSurfaceCreated) {
955                if (mReportedVisible) {
956                    if (DEBUG) Log.v(TAG, "Offsets change in " + this
957                            + ": " + xOffset + "," + yOffset);
958                    final int availw = mIWallpaperEngine.mReqWidth-mCurWidth;
959                    final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0;
960                    final int availh = mIWallpaperEngine.mReqHeight-mCurHeight;
961                    final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0;
962                    onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels);
963                } else {
964                    mOffsetsChanged = true;
965                }
966            }
967
968            if (sync) {
969                try {
970                    if (DEBUG) Log.v(TAG, "Reporting offsets change complete");
971                    mSession.wallpaperOffsetsComplete(mWindow.asBinder());
972                } catch (RemoteException e) {
973                }
974            }
975        }
976
977        void doCommand(WallpaperCommand cmd) {
978            Bundle result;
979            if (!mDestroyed) {
980                result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z,
981                        cmd.extras, cmd.sync);
982            } else {
983                result = null;
984            }
985            if (cmd.sync) {
986                try {
987                    if (DEBUG) Log.v(TAG, "Reporting command complete");
988                    mSession.wallpaperCommandComplete(mWindow.asBinder(), result);
989                } catch (RemoteException e) {
990                }
991            }
992        }
993
994        void reportSurfaceDestroyed() {
995            if (mSurfaceCreated) {
996                mSurfaceCreated = false;
997                mSurfaceHolder.ungetCallbacks();
998                SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
999                if (callbacks != null) {
1000                    for (SurfaceHolder.Callback c : callbacks) {
1001                        c.surfaceDestroyed(mSurfaceHolder);
1002                    }
1003                }
1004                if (DEBUG) Log.v(TAG, "onSurfaceDestroyed("
1005                        + mSurfaceHolder + "): " + this);
1006                onSurfaceDestroyed(mSurfaceHolder);
1007            }
1008        }
1009
1010        void detach() {
1011            if (mDestroyed) {
1012                return;
1013            }
1014
1015            mDestroyed = true;
1016
1017            if (mDisplayManager != null) {
1018                mDisplayManager.unregisterDisplayListener(mDisplayListener);
1019            }
1020
1021            if (mVisible) {
1022                mVisible = false;
1023                if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this);
1024                onVisibilityChanged(false);
1025            }
1026
1027            reportSurfaceDestroyed();
1028
1029            if (DEBUG) Log.v(TAG, "onDestroy(): " + this);
1030            onDestroy();
1031
1032            if (mCreated) {
1033                try {
1034                    if (DEBUG) Log.v(TAG, "Removing window and destroying surface "
1035                            + mSurfaceHolder.getSurface() + " of: " + this);
1036
1037                    if (mInputEventReceiver != null) {
1038                        mInputEventReceiver.dispose();
1039                        mInputEventReceiver = null;
1040                    }
1041
1042                    mSession.remove(mWindow);
1043                } catch (RemoteException e) {
1044                }
1045                mSurfaceHolder.mSurface.release();
1046                mCreated = false;
1047
1048                // Dispose the input channel after removing the window so the Window Manager
1049                // doesn't interpret the input channel being closed as an abnormal termination.
1050                if (mInputChannel != null) {
1051                    mInputChannel.dispose();
1052                    mInputChannel = null;
1053                }
1054            }
1055        }
1056
1057        private final DisplayListener mDisplayListener = new DisplayListener() {
1058            @Override
1059            public void onDisplayChanged(int displayId) {
1060                if (mDisplay.getDisplayId() == displayId) {
1061                    reportVisibility();
1062                }
1063            }
1064
1065            @Override
1066            public void onDisplayRemoved(int displayId) {
1067            }
1068
1069            @Override
1070            public void onDisplayAdded(int displayId) {
1071            }
1072        };
1073    }
1074
1075    class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
1076            implements HandlerCaller.Callback {
1077        private final HandlerCaller mCaller;
1078
1079        final IWallpaperConnection mConnection;
1080        final IBinder mWindowToken;
1081        final int mWindowType;
1082        final boolean mIsPreview;
1083        boolean mShownReported;
1084        int mReqWidth;
1085        int mReqHeight;
1086        final Rect mDisplayPadding = new Rect();
1087
1088        Engine mEngine;
1089
1090        IWallpaperEngineWrapper(WallpaperService context,
1091                IWallpaperConnection conn, IBinder windowToken,
1092                int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
1093            mCaller = new HandlerCaller(context, context.getMainLooper(), this, true);
1094            mConnection = conn;
1095            mWindowToken = windowToken;
1096            mWindowType = windowType;
1097            mIsPreview = isPreview;
1098            mReqWidth = reqWidth;
1099            mReqHeight = reqHeight;
1100            mDisplayPadding.set(padding);
1101
1102            Message msg = mCaller.obtainMessage(DO_ATTACH);
1103            mCaller.sendMessage(msg);
1104        }
1105
1106        public void setDesiredSize(int width, int height) {
1107            Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height);
1108            mCaller.sendMessage(msg);
1109        }
1110
1111        public void setDisplayPadding(Rect padding) {
1112            Message msg = mCaller.obtainMessageO(DO_SET_DISPLAY_PADDING, padding);
1113            mCaller.sendMessage(msg);
1114        }
1115
1116        public void setVisibility(boolean visible) {
1117            Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
1118                    visible ? 1 : 0);
1119            mCaller.sendMessage(msg);
1120        }
1121
1122        public void dispatchPointer(MotionEvent event) {
1123            if (mEngine != null) {
1124                mEngine.dispatchPointer(event);
1125            } else {
1126                event.recycle();
1127            }
1128        }
1129
1130        public void dispatchWallpaperCommand(String action, int x, int y,
1131                int z, Bundle extras) {
1132            if (mEngine != null) {
1133                mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false);
1134            }
1135        }
1136
1137        public void reportShown() {
1138            if (!mShownReported) {
1139                mShownReported = true;
1140                try {
1141                    mConnection.engineShown(this);
1142                } catch (RemoteException e) {
1143                    Log.w(TAG, "Wallpaper host disappeared", e);
1144                    return;
1145                }
1146            }
1147        }
1148
1149        public void destroy() {
1150            Message msg = mCaller.obtainMessage(DO_DETACH);
1151            mCaller.sendMessage(msg);
1152        }
1153
1154        public void executeMessage(Message message) {
1155            switch (message.what) {
1156                case DO_ATTACH: {
1157                    try {
1158                        mConnection.attachEngine(this);
1159                    } catch (RemoteException e) {
1160                        Log.w(TAG, "Wallpaper host disappeared", e);
1161                        return;
1162                    }
1163                    Engine engine = onCreateEngine();
1164                    mEngine = engine;
1165                    mActiveEngines.add(engine);
1166                    engine.attach(this);
1167                    return;
1168                }
1169                case DO_DETACH: {
1170                    mActiveEngines.remove(mEngine);
1171                    mEngine.detach();
1172                    return;
1173                }
1174                case DO_SET_DESIRED_SIZE: {
1175                    mEngine.doDesiredSizeChanged(message.arg1, message.arg2);
1176                    return;
1177                }
1178                case DO_SET_DISPLAY_PADDING: {
1179                    mEngine.doDisplayPaddingChanged((Rect) message.obj);
1180                }
1181                case MSG_UPDATE_SURFACE:
1182                    mEngine.updateSurface(true, false, false);
1183                    break;
1184                case MSG_VISIBILITY_CHANGED:
1185                    if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
1186                            + ": " + message.arg1);
1187                    mEngine.doVisibilityChanged(message.arg1 != 0);
1188                    break;
1189                case MSG_WALLPAPER_OFFSETS: {
1190                    mEngine.doOffsetsChanged(true);
1191                } break;
1192                case MSG_WALLPAPER_COMMAND: {
1193                    WallpaperCommand cmd = (WallpaperCommand)message.obj;
1194                    mEngine.doCommand(cmd);
1195                } break;
1196                case MSG_WINDOW_RESIZED: {
1197                    final boolean reportDraw = message.arg1 != 0;
1198                    mEngine.mOutsets.set((Rect) message.obj);
1199                    mEngine.updateSurface(true, false, reportDraw);
1200                    mEngine.doOffsetsChanged(true);
1201                } break;
1202                case MSG_WINDOW_MOVED: {
1203                    // Do nothing. What does it mean for a Wallpaper to move?
1204                } break;
1205                case MSG_TOUCH_EVENT: {
1206                    boolean skip = false;
1207                    MotionEvent ev = (MotionEvent)message.obj;
1208                    if (ev.getAction() == MotionEvent.ACTION_MOVE) {
1209                        synchronized (mEngine.mLock) {
1210                            if (mEngine.mPendingMove == ev) {
1211                                mEngine.mPendingMove = null;
1212                            } else {
1213                                // this is not the motion event we are looking for....
1214                                skip = true;
1215                            }
1216                        }
1217                    }
1218                    if (!skip) {
1219                        if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev);
1220                        mEngine.onTouchEvent(ev);
1221                    }
1222                    ev.recycle();
1223                } break;
1224                default :
1225                    Log.w(TAG, "Unknown message type " + message.what);
1226            }
1227        }
1228    }
1229
1230    /**
1231     * Implements the internal {@link IWallpaperService} interface to convert
1232     * incoming calls to it back to calls on an {@link WallpaperService}.
1233     */
1234    class IWallpaperServiceWrapper extends IWallpaperService.Stub {
1235        private final WallpaperService mTarget;
1236
1237        public IWallpaperServiceWrapper(WallpaperService context) {
1238            mTarget = context;
1239        }
1240
1241        @Override
1242        public void attach(IWallpaperConnection conn, IBinder windowToken,
1243                int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
1244            new IWallpaperEngineWrapper(mTarget, conn, windowToken,
1245                    windowType, isPreview, reqWidth, reqHeight, padding);
1246        }
1247    }
1248
1249    @Override
1250    public void onCreate() {
1251        super.onCreate();
1252    }
1253
1254    @Override
1255    public void onDestroy() {
1256        super.onDestroy();
1257        for (int i=0; i<mActiveEngines.size(); i++) {
1258            mActiveEngines.get(i).detach();
1259        }
1260        mActiveEngines.clear();
1261    }
1262
1263    /**
1264     * Implement to return the implementation of the internal accessibility
1265     * service interface.  Subclasses should not override.
1266     */
1267    @Override
1268    public final IBinder onBind(Intent intent) {
1269        return new IWallpaperServiceWrapper(this);
1270    }
1271
1272    /**
1273     * Must be implemented to return a new instance of the wallpaper's engine.
1274     * Note that multiple instances may be active at the same time, such as
1275     * when the wallpaper is currently set as the active wallpaper and the user
1276     * is in the wallpaper picker viewing a preview of it as well.
1277     */
1278    public abstract Engine onCreateEngine();
1279
1280    @Override
1281    protected void dump(FileDescriptor fd, PrintWriter out, String[] args) {
1282        out.print("State of wallpaper "); out.print(this); out.println(":");
1283        for (int i=0; i<mActiveEngines.size(); i++) {
1284            Engine engine = mActiveEngines.get(i);
1285            out.print("  Engine "); out.print(engine); out.println(":");
1286            engine.dump("    ", fd, out, args);
1287        }
1288    }
1289}
1290