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