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