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