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