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