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