WallpaperService.java revision b047b8bd7e363081e91ba6cbc8d09cd355624584
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, int displayId) {
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