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