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