OSDN Git Service

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