WallpaperService.java revision 527de8e3de4c2bad9ba0fc0e50f8fb3f95ba4681
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 com.android.internal.os.HandlerCaller;
20import com.android.internal.view.BaseIWindow;
21import com.android.internal.view.BaseInputHandler;
22import com.android.internal.view.BaseSurfaceHolder;
23
24import android.annotation.SdkConstant;
25import android.annotation.SdkConstant.SdkConstantType;
26import android.app.Service;
27import android.app.WallpaperManager;
28import android.content.BroadcastReceiver;
29import android.content.Context;
30import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.res.Configuration;
33import android.graphics.PixelFormat;
34import android.graphics.Rect;
35import android.os.Bundle;
36import android.os.IBinder;
37import android.os.Looper;
38import android.os.Message;
39import android.os.Process;
40import android.os.RemoteException;
41import android.util.Log;
42import android.util.LogPrinter;
43import android.view.Gravity;
44import android.view.IWindowSession;
45import android.view.InputChannel;
46import android.view.InputDevice;
47import android.view.InputHandler;
48import android.view.InputQueue;
49import android.view.MotionEvent;
50import android.view.SurfaceHolder;
51import android.view.View;
52import android.view.ViewGroup;
53import android.view.ViewRootImpl;
54import android.view.WindowManager;
55import android.view.WindowManagerImpl;
56
57import java.io.FileDescriptor;
58import java.io.PrintWriter;
59import java.util.ArrayList;
60
61/**
62 * A wallpaper service is responsible for showing a live wallpaper behind
63 * applications that would like to sit on top of it.  This service object
64 * itself does very little -- its only purpose is to generate instances of
65 * {@link Engine} as needed.  Implementing a wallpaper thus
66 * involves subclassing from this, subclassing an Engine implementation,
67 * and implementing {@link #onCreateEngine()} to return a new instance of
68 * your engine.
69 */
70public abstract class WallpaperService extends Service {
71    /**
72     * The {@link Intent} that must be declared as handled by the service.
73     * To be supported, the service must also require the
74     * {@link android.Manifest.permission#BIND_WALLPAPER} permission so
75     * that other applications can not abuse it.
76     */
77    @SdkConstant(SdkConstantType.SERVICE_ACTION)
78    public static final String SERVICE_INTERFACE =
79            "android.service.wallpaper.WallpaperService";
80
81    /**
82     * Name under which a WallpaperService component publishes information
83     * about itself.  This meta-data must reference an XML resource containing
84     * a <code>&lt;{@link android.R.styleable#Wallpaper wallpaper}&gt;</code>
85     * tag.
86     */
87    public static final String SERVICE_META_DATA = "android.service.wallpaper";
88
89    static final String TAG = "WallpaperService";
90    static final boolean DEBUG = false;
91
92    private static final int DO_ATTACH = 10;
93    private static final int DO_DETACH = 20;
94    private static final int DO_SET_DESIRED_SIZE = 30;
95
96    private static final int MSG_UPDATE_SURFACE = 10000;
97    private static final int MSG_VISIBILITY_CHANGED = 10010;
98    private static final int MSG_WALLPAPER_OFFSETS = 10020;
99    private static final int MSG_WALLPAPER_COMMAND = 10025;
100    private static final int MSG_WINDOW_RESIZED = 10030;
101    private static final int MSG_TOUCH_EVENT = 10040;
102
103    private Looper mCallbackLooper;
104    private final ArrayList<Engine> mActiveEngines
105            = new ArrayList<Engine>();
106
107    static final class WallpaperCommand {
108        String action;
109        int x;
110        int y;
111        int z;
112        Bundle extras;
113        boolean sync;
114    }
115
116    /**
117     * The actual implementation of a wallpaper.  A wallpaper service may
118     * have multiple instances running (for example as a real wallpaper
119     * and as a preview), each of which is represented by its own Engine
120     * instance.  You must implement {@link WallpaperService#onCreateEngine()}
121     * to return your concrete Engine implementation.
122     */
123    public class Engine {
124        IWallpaperEngineWrapper mIWallpaperEngine;
125
126        // Copies from mIWallpaperEngine.
127        HandlerCaller mCaller;
128        IWallpaperConnection mConnection;
129        IBinder mWindowToken;
130
131        boolean mInitializing = true;
132        boolean mVisible;
133        boolean mScreenOn = true;
134        boolean mReportedVisible;
135        boolean mDestroyed;
136
137        // Current window state.
138        boolean mCreated;
139        boolean mSurfaceCreated;
140        boolean mIsCreating;
141        boolean mDrawingAllowed;
142        int mWidth;
143        int mHeight;
144        int mFormat;
145        int mType;
146        int mCurWidth;
147        int mCurHeight;
148        int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
149        int mCurWindowFlags = mWindowFlags;
150        final Rect mVisibleInsets = new Rect();
151        final Rect mWinFrame = new Rect();
152        final Rect mContentInsets = new Rect();
153        final Configuration mConfiguration = new Configuration();
154
155        final WindowManager.LayoutParams mLayout
156                = new WindowManager.LayoutParams();
157        IWindowSession mSession;
158        InputChannel mInputChannel;
159
160        final Object mLock = new Object();
161        boolean mOffsetMessageEnqueued;
162        float mPendingXOffset;
163        float mPendingYOffset;
164        float mPendingXOffsetStep;
165        float mPendingYOffsetStep;
166        boolean mPendingSync;
167        MotionEvent mPendingMove;
168
169        final BroadcastReceiver mReceiver = new BroadcastReceiver() {
170            @Override
171            public void onReceive(Context context, Intent intent) {
172                if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
173                    mScreenOn = true;
174                    reportVisibility();
175                } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
176                    mScreenOn = false;
177                    reportVisibility();
178                }
179            }
180        };
181
182        final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
183            {
184                mRequestedFormat = PixelFormat.RGBX_8888;
185            }
186
187            @Override
188            public boolean onAllowLockCanvas() {
189                return mDrawingAllowed;
190            }
191
192            @Override
193            public void onRelayoutContainer() {
194                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
195                mCaller.sendMessage(msg);
196            }
197
198            @Override
199            public void onUpdateSurface() {
200                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
201                mCaller.sendMessage(msg);
202            }
203
204            public boolean isCreating() {
205                return mIsCreating;
206            }
207
208            @Override
209            public void setFixedSize(int width, int height) {
210                if (Process.myUid() != Process.SYSTEM_UID) {
211                    // Regular apps can't do this.  It can only work for
212                    // certain designs of window animations, so you can't
213                    // rely on it.
214                    throw new UnsupportedOperationException(
215                            "Wallpapers currently only support sizing from layout");
216                }
217                super.setFixedSize(width, height);
218            }
219
220            public void setKeepScreenOn(boolean screenOn) {
221                throw new UnsupportedOperationException(
222                        "Wallpapers do not support keep screen on");
223            }
224
225        };
226
227        final InputHandler mInputHandler = new BaseInputHandler() {
228            @Override
229            public void handleMotion(MotionEvent event,
230                    InputQueue.FinishedCallback finishedCallback) {
231                boolean handled = false;
232                try {
233                    int source = event.getSource();
234                    if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
235                        dispatchPointer(event);
236                        handled = true;
237                    }
238                } finally {
239                    finishedCallback.finished(handled);
240                }
241            }
242        };
243
244        final BaseIWindow mWindow = new BaseIWindow() {
245            @Override
246            public void resized(int w, int h, Rect coveredInsets,
247                    Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
248                Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
249                        reportDraw ? 1 : 0);
250                mCaller.sendMessage(msg);
251            }
252
253            @Override
254            public void dispatchAppVisibility(boolean visible) {
255                // We don't do this in preview mode; we'll let the preview
256                // activity tell us when to run.
257                if (!mIWallpaperEngine.mIsPreview) {
258                    Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
259                            visible ? 1 : 0);
260                    mCaller.sendMessage(msg);
261                }
262            }
263
264            @Override
265            public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
266                    boolean sync) {
267                synchronized (mLock) {
268                    if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y);
269                    mPendingXOffset = x;
270                    mPendingYOffset = y;
271                    mPendingXOffsetStep = xStep;
272                    mPendingYOffsetStep = yStep;
273                    if (sync) {
274                        mPendingSync = true;
275                    }
276                    if (!mOffsetMessageEnqueued) {
277                        mOffsetMessageEnqueued = true;
278                        Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS);
279                        mCaller.sendMessage(msg);
280                    }
281                }
282            }
283
284            public void dispatchWallpaperCommand(String action, int x, int y,
285                    int z, Bundle extras, boolean sync) {
286                synchronized (mLock) {
287                    if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y);
288                    WallpaperCommand cmd = new WallpaperCommand();
289                    cmd.action = action;
290                    cmd.x = x;
291                    cmd.y = y;
292                    cmd.z = z;
293                    cmd.extras = extras;
294                    cmd.sync = sync;
295                    Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND);
296                    msg.obj = cmd;
297                    mCaller.sendMessage(msg);
298                }
299            }
300        };
301
302        /**
303         * Provides access to the surface in which this wallpaper is drawn.
304         */
305        public SurfaceHolder getSurfaceHolder() {
306            return mSurfaceHolder;
307        }
308
309        /**
310         * Convenience for {@link WallpaperManager#getDesiredMinimumWidth()
311         * WallpaperManager.getDesiredMinimumWidth()}, returning the width
312         * that the system would like this wallpaper to run in.
313         */
314        public int getDesiredMinimumWidth() {
315            return mIWallpaperEngine.mReqWidth;
316        }
317
318        /**
319         * Convenience for {@link WallpaperManager#getDesiredMinimumHeight()
320         * WallpaperManager.getDesiredMinimumHeight()}, returning the height
321         * that the system would like this wallpaper to run in.
322         */
323        public int getDesiredMinimumHeight() {
324            return mIWallpaperEngine.mReqHeight;
325        }
326
327        /**
328         * Return whether the wallpaper is currently visible to the user,
329         * this is the last value supplied to
330         * {@link #onVisibilityChanged(boolean)}.
331         */
332        public boolean isVisible() {
333            return mReportedVisible;
334        }
335
336        /**
337         * Returns true if this engine is running in preview mode -- that is,
338         * it is being shown to the user before they select it as the actual
339         * wallpaper.
340         */
341        public boolean isPreview() {
342            return mIWallpaperEngine.mIsPreview;
343        }
344
345        /**
346         * Control whether this wallpaper will receive raw touch events
347         * from the window manager as the user interacts with the window
348         * that is currently displaying the wallpaper.  By default they
349         * are turned off.  If enabled, the events will be received in
350         * {@link #onTouchEvent(MotionEvent)}.
351         */
352        public void setTouchEventsEnabled(boolean enabled) {
353            mWindowFlags = enabled
354                    ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
355                    : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
356            if (mCreated) {
357                updateSurface(false, false, false);
358            }
359        }
360
361        /**
362         * Called once to initialize the engine.  After returning, the
363         * engine's surface will be created by the framework.
364         */
365        public void onCreate(SurfaceHolder surfaceHolder) {
366        }
367
368        /**
369         * Called right before the engine is going away.  After this the
370         * surface will be destroyed and this Engine object is no longer
371         * valid.
372         */
373        public void onDestroy() {
374        }
375
376        /**
377         * Called to inform you of the wallpaper becoming visible or
378         * hidden.  <em>It is very important that a wallpaper only use
379         * CPU while it is visible.</em>.
380         */
381        public void onVisibilityChanged(boolean visible) {
382        }
383
384        /**
385         * Called as the user performs touch-screen interaction with the
386         * window that is currently showing this wallpaper.  Note that the
387         * events you receive here are driven by the actual application the
388         * user is interacting with, so if it is slow you will get fewer
389         * move events.
390         */
391        public void onTouchEvent(MotionEvent event) {
392        }
393
394        /**
395         * Called to inform you of the wallpaper's offsets changing
396         * within its contain, corresponding to the container's
397         * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float)
398         * WallpaperManager.setWallpaperOffsets()}.
399         */
400        public void onOffsetsChanged(float xOffset, float yOffset,
401                float xOffsetStep, float yOffsetStep,
402                int xPixelOffset, int yPixelOffset) {
403        }
404
405        /**
406         * Process a command that was sent to the wallpaper with
407         * {@link WallpaperManager#sendWallpaperCommand}.
408         * The default implementation does nothing, and always returns null
409         * as the result.
410         *
411         * @param action The name of the command to perform.  This tells you
412         * what to do and how to interpret the rest of the arguments.
413         * @param x Generic integer parameter.
414         * @param y Generic integer parameter.
415         * @param z Generic integer parameter.
416         * @param extras Any additional parameters.
417         * @param resultRequested If true, the caller is requesting that
418         * a result, appropriate for the command, be returned back.
419         * @return If returning a result, create a Bundle and place the
420         * result data in to it.  Otherwise return null.
421         */
422        public Bundle onCommand(String action, int x, int y, int z,
423                Bundle extras, boolean resultRequested) {
424            return null;
425        }
426
427        /**
428         * Called when an application has changed the desired virtual size of
429         * the wallpaper.
430         */
431        public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
432        }
433
434        /**
435         * Convenience for {@link SurfaceHolder.Callback#surfaceChanged
436         * SurfaceHolder.Callback.surfaceChanged()}.
437         */
438        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
439        }
440
441        /**
442         * Convenience for {@link SurfaceHolder.Callback2#surfaceRedrawNeeded
443         * SurfaceHolder.Callback.surfaceRedrawNeeded()}.
444         */
445        public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
446        }
447
448        /**
449         * Convenience for {@link SurfaceHolder.Callback#surfaceCreated
450         * SurfaceHolder.Callback.surfaceCreated()}.
451         */
452        public void onSurfaceCreated(SurfaceHolder holder) {
453        }
454
455        /**
456         * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed
457         * SurfaceHolder.Callback.surfaceDestroyed()}.
458         */
459        public void onSurfaceDestroyed(SurfaceHolder holder) {
460        }
461
462        protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
463            out.print(prefix); out.print("mInitializing="); out.print(mInitializing);
464                    out.print(" mDestroyed="); out.println(mDestroyed);
465            out.print(prefix); out.print("mVisible="); out.print(mVisible);
466                    out.print(" mScreenOn="); out.print(mScreenOn);
467                    out.print(" mReportedVisible="); out.println(mReportedVisible);
468            out.print(prefix); out.print("mCreated="); out.print(mCreated);
469                    out.print(" mSurfaceCreated="); out.print(mSurfaceCreated);
470                    out.print(" mIsCreating="); out.print(mIsCreating);
471                    out.print(" mDrawingAllowed="); out.println(mDrawingAllowed);
472            out.print(prefix); out.print("mWidth="); out.print(mWidth);
473                    out.print(" mCurWidth="); out.print(mCurWidth);
474                    out.print(" mHeight="); out.print(mHeight);
475                    out.print(" mCurHeight="); out.println(mCurHeight);
476            out.print(prefix); out.print("mType="); out.print(mType);
477                    out.print(" mWindowFlags="); out.print(mWindowFlags);
478                    out.print(" mCurWindowFlags="); out.println(mCurWindowFlags);
479            out.print(prefix); out.print("mVisibleInsets=");
480                    out.print(mVisibleInsets.toShortString());
481                    out.print(" mWinFrame="); out.print(mWinFrame.toShortString());
482                    out.print(" mContentInsets="); out.println(mContentInsets.toShortString());
483            out.print(prefix); out.print("mConfiguration="); out.println(mConfiguration);
484            out.print(prefix); out.print("mLayout="); out.println(mLayout);
485            synchronized (mLock) {
486                out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
487                        out.print(" mPendingXOffset="); out.println(mPendingXOffset);
488                out.print(prefix); out.print("mPendingXOffsetStep=");
489                        out.print(mPendingXOffsetStep);
490                        out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep);
491                out.print(prefix); out.print("mOffsetMessageEnqueued=");
492                        out.print(mOffsetMessageEnqueued);
493                        out.print(" mPendingSync="); out.println(mPendingSync);
494                if (mPendingMove != null) {
495                    out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove);
496                }
497            }
498        }
499
500        private void dispatchPointer(MotionEvent event) {
501            if (event.isTouchEvent()) {
502                synchronized (mLock) {
503                    if (event.getAction() == MotionEvent.ACTION_MOVE) {
504                        mPendingMove = event;
505                    } else {
506                        mPendingMove = null;
507                    }
508                }
509                Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
510                mCaller.sendMessage(msg);
511            }
512        }
513
514        void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
515            if (mDestroyed) {
516                Log.w(TAG, "Ignoring updateSurface: destroyed");
517            }
518
519            int myWidth = mSurfaceHolder.getRequestedWidth();
520            if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT;
521            int myHeight = mSurfaceHolder.getRequestedHeight();
522            if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT;
523
524            final boolean creating = !mCreated;
525            final boolean surfaceCreating = !mSurfaceCreated;
526            final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
527            boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
528            final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
529            final boolean flagsChanged = mCurWindowFlags != mWindowFlags;
530            if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged
531                    || typeChanged || flagsChanged || redrawNeeded) {
532
533                if (DEBUG) Log.v(TAG, "Changes: creating=" + creating
534                        + " format=" + formatChanged + " size=" + sizeChanged);
535
536                try {
537                    mWidth = myWidth;
538                    mHeight = myHeight;
539                    mFormat = mSurfaceHolder.getRequestedFormat();
540                    mType = mSurfaceHolder.getRequestedType();
541
542                    mLayout.x = 0;
543                    mLayout.y = 0;
544                    mLayout.width = myWidth;
545                    mLayout.height = myHeight;
546
547                    mLayout.format = mFormat;
548
549                    mCurWindowFlags = mWindowFlags;
550                    mLayout.flags = mWindowFlags
551                            | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
552                            | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
553                            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
554                            ;
555
556                    mLayout.memoryType = mType;
557                    mLayout.token = mWindowToken;
558
559                    if (!mCreated) {
560                        mLayout.type = mIWallpaperEngine.mWindowType;
561                        mLayout.gravity = Gravity.LEFT|Gravity.TOP;
562                        mLayout.setTitle(WallpaperService.this.getClass().getName());
563                        mLayout.windowAnimations =
564                                com.android.internal.R.style.Animation_Wallpaper;
565                        mInputChannel = new InputChannel();
566                        if (mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets,
567                                mInputChannel) < 0) {
568                            Log.w(TAG, "Failed to add window while updating wallpaper surface.");
569                            return;
570                        }
571                        mCreated = true;
572
573                        InputQueue.registerInputChannel(mInputChannel, mInputHandler,
574                                Looper.myQueue());
575                    }
576
577                    mSurfaceHolder.mSurfaceLock.lock();
578                    mDrawingAllowed = true;
579
580                    final int relayoutResult = mSession.relayout(
581                        mWindow, mLayout, mWidth, mHeight,
582                            View.VISIBLE, false, mWinFrame, mContentInsets,
583                            mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface);
584
585                    if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
586                            + ", frame=" + mWinFrame);
587
588                    int w = mWinFrame.width();
589                    if (mCurWidth != w) {
590                        sizeChanged = true;
591                        mCurWidth = w;
592                    }
593                    int h = mWinFrame.height();
594                    if (mCurHeight != h) {
595                        sizeChanged = true;
596                        mCurHeight = h;
597                    }
598
599                    mSurfaceHolder.setSurfaceFrameSize(w, h);
600                    mSurfaceHolder.mSurfaceLock.unlock();
601
602                    if (!mSurfaceHolder.mSurface.isValid()) {
603                        reportSurfaceDestroyed();
604                        if (DEBUG) Log.v(TAG, "Layout: Surface destroyed");
605                        return;
606                    }
607
608                    try {
609                        mSurfaceHolder.ungetCallbacks();
610
611                        if (surfaceCreating) {
612                            mIsCreating = true;
613                            if (DEBUG) Log.v(TAG, "onSurfaceCreated("
614                                    + mSurfaceHolder + "): " + this);
615                            onSurfaceCreated(mSurfaceHolder);
616                            SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
617                            if (callbacks != null) {
618                                for (SurfaceHolder.Callback c : callbacks) {
619                                    c.surfaceCreated(mSurfaceHolder);
620                                }
621                            }
622                        }
623
624                        redrawNeeded |= creating
625                                || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0;
626
627                        if (forceReport || creating || surfaceCreating
628                                || formatChanged || sizeChanged) {
629                            if (DEBUG) {
630                                RuntimeException e = new RuntimeException();
631                                e.fillInStackTrace();
632                                Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating
633                                        + " formatChanged=" + formatChanged
634                                        + " sizeChanged=" + sizeChanged, e);
635                            }
636                            if (DEBUG) Log.v(TAG, "onSurfaceChanged("
637                                    + mSurfaceHolder + ", " + mFormat
638                                    + ", " + mCurWidth + ", " + mCurHeight
639                                    + "): " + this);
640                            onSurfaceChanged(mSurfaceHolder, mFormat,
641                                    mCurWidth, mCurHeight);
642                            SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
643                            if (callbacks != null) {
644                                for (SurfaceHolder.Callback c : callbacks) {
645                                    c.surfaceChanged(mSurfaceHolder, mFormat,
646                                            mCurWidth, mCurHeight);
647                                }
648                            }
649                        }
650
651                        if (redrawNeeded) {
652                            onSurfaceRedrawNeeded(mSurfaceHolder);
653                            SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
654                            if (callbacks != null) {
655                                for (SurfaceHolder.Callback c : callbacks) {
656                                    if (c instanceof SurfaceHolder.Callback2) {
657                                        ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
658                                                mSurfaceHolder);
659                                    }
660                                }
661                            }
662                        }
663
664                    } finally {
665                        mIsCreating = false;
666                        mSurfaceCreated = true;
667                        if (redrawNeeded) {
668                            mSession.finishDrawing(mWindow);
669                        }
670                    }
671                } catch (RemoteException ex) {
672                }
673                if (DEBUG) Log.v(
674                    TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
675                    " w=" + mLayout.width + " h=" + mLayout.height);
676            }
677        }
678
679        void attach(IWallpaperEngineWrapper wrapper) {
680            if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper);
681            if (mDestroyed) {
682                return;
683            }
684
685            mIWallpaperEngine = wrapper;
686            mCaller = wrapper.mCaller;
687            mConnection = wrapper.mConnection;
688            mWindowToken = wrapper.mWindowToken;
689            mSurfaceHolder.setSizeFromLayout();
690            mInitializing = true;
691            mSession = ViewRootImpl.getWindowSession(getMainLooper());
692
693            mWindow.setSession(mSession);
694
695            IntentFilter filter = new IntentFilter();
696            filter.addAction(Intent.ACTION_SCREEN_ON);
697            filter.addAction(Intent.ACTION_SCREEN_OFF);
698            registerReceiver(mReceiver, filter);
699
700            if (DEBUG) Log.v(TAG, "onCreate(): " + this);
701            onCreate(mSurfaceHolder);
702
703            mInitializing = false;
704            updateSurface(false, false, false);
705        }
706
707        void doDesiredSizeChanged(int desiredWidth, int desiredHeight) {
708            if (!mDestroyed) {
709                if (DEBUG) Log.v(TAG, "onDesiredSizeChanged("
710                        + desiredWidth + "," + desiredHeight + "): " + this);
711                mIWallpaperEngine.mReqWidth = desiredWidth;
712                mIWallpaperEngine.mReqHeight = desiredHeight;
713                onDesiredSizeChanged(desiredWidth, desiredHeight);
714                doOffsetsChanged();
715            }
716        }
717
718        void doVisibilityChanged(boolean visible) {
719            if (!mDestroyed) {
720                mVisible = visible;
721                reportVisibility();
722            }
723        }
724
725        void reportVisibility() {
726            if (!mDestroyed) {
727                boolean visible = mVisible && mScreenOn;
728                if (mReportedVisible != visible) {
729                    mReportedVisible = visible;
730                    if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible
731                            + "): " + this);
732                    if (visible) {
733                        // If becoming visible, in preview mode the surface
734                        // may have been destroyed so now we need to make
735                        // sure it is re-created.
736                        updateSurface(false, false, false);
737                    }
738                    onVisibilityChanged(visible);
739                }
740            }
741        }
742
743        void doOffsetsChanged() {
744            if (mDestroyed) {
745                return;
746            }
747
748            float xOffset;
749            float yOffset;
750            float xOffsetStep;
751            float yOffsetStep;
752            boolean sync;
753            synchronized (mLock) {
754                xOffset = mPendingXOffset;
755                yOffset = mPendingYOffset;
756                xOffsetStep = mPendingXOffsetStep;
757                yOffsetStep = mPendingYOffsetStep;
758                sync = mPendingSync;
759                mPendingSync = false;
760                mOffsetMessageEnqueued = false;
761            }
762
763            if (mSurfaceCreated) {
764                if (DEBUG) Log.v(TAG, "Offsets change in " + this
765                        + ": " + xOffset + "," + yOffset);
766                final int availw = mIWallpaperEngine.mReqWidth-mCurWidth;
767                final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0;
768                final int availh = mIWallpaperEngine.mReqHeight-mCurHeight;
769                final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0;
770                onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels);
771            }
772
773            if (sync) {
774                try {
775                    if (DEBUG) Log.v(TAG, "Reporting offsets change complete");
776                    mSession.wallpaperOffsetsComplete(mWindow.asBinder());
777                } catch (RemoteException e) {
778                }
779            }
780        }
781
782        void doCommand(WallpaperCommand cmd) {
783            Bundle result;
784            if (!mDestroyed) {
785                result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z,
786                        cmd.extras, cmd.sync);
787            } else {
788                result = null;
789            }
790            if (cmd.sync) {
791                try {
792                    if (DEBUG) Log.v(TAG, "Reporting command complete");
793                    mSession.wallpaperCommandComplete(mWindow.asBinder(), result);
794                } catch (RemoteException e) {
795                }
796            }
797        }
798
799        void reportSurfaceDestroyed() {
800            if (mSurfaceCreated) {
801                mSurfaceCreated = false;
802                mSurfaceHolder.ungetCallbacks();
803                SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
804                if (callbacks != null) {
805                    for (SurfaceHolder.Callback c : callbacks) {
806                        c.surfaceDestroyed(mSurfaceHolder);
807                    }
808                }
809                if (DEBUG) Log.v(TAG, "onSurfaceDestroyed("
810                        + mSurfaceHolder + "): " + this);
811                onSurfaceDestroyed(mSurfaceHolder);
812            }
813        }
814
815        void detach() {
816            if (mDestroyed) {
817                return;
818            }
819
820            mDestroyed = true;
821
822            if (mVisible) {
823                mVisible = false;
824                if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this);
825                onVisibilityChanged(false);
826            }
827
828            reportSurfaceDestroyed();
829
830            if (DEBUG) Log.v(TAG, "onDestroy(): " + this);
831            onDestroy();
832
833            unregisterReceiver(mReceiver);
834
835            if (mCreated) {
836                try {
837                    if (DEBUG) Log.v(TAG, "Removing window and destroying surface "
838                            + mSurfaceHolder.getSurface() + " of: " + this);
839
840                    if (mInputChannel != null) {
841                        InputQueue.unregisterInputChannel(mInputChannel);
842                    }
843
844                    mSession.remove(mWindow);
845                } catch (RemoteException e) {
846                }
847                mSurfaceHolder.mSurface.release();
848                mCreated = false;
849
850                // Dispose the input channel after removing the window so the Window Manager
851                // doesn't interpret the input channel being closed as an abnormal termination.
852                if (mInputChannel != null) {
853                    mInputChannel.dispose();
854                    mInputChannel = null;
855                }
856            }
857        }
858    }
859
860    class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
861            implements HandlerCaller.Callback {
862        private final HandlerCaller mCaller;
863
864        final IWallpaperConnection mConnection;
865        final IBinder mWindowToken;
866        final int mWindowType;
867        final boolean mIsPreview;
868        int mReqWidth;
869        int mReqHeight;
870
871        Engine mEngine;
872
873        IWallpaperEngineWrapper(WallpaperService context,
874                IWallpaperConnection conn, IBinder windowToken,
875                int windowType, boolean isPreview, int reqWidth, int reqHeight) {
876            if (DEBUG && mCallbackLooper != null) {
877                mCallbackLooper.setMessageLogging(new LogPrinter(Log.VERBOSE, TAG));
878            }
879            mCaller = new HandlerCaller(context,
880                    mCallbackLooper != null
881                            ? mCallbackLooper : context.getMainLooper(),
882                    this);
883            mConnection = conn;
884            mWindowToken = windowToken;
885            mWindowType = windowType;
886            mIsPreview = isPreview;
887            mReqWidth = reqWidth;
888            mReqHeight = reqHeight;
889
890            Message msg = mCaller.obtainMessage(DO_ATTACH);
891            mCaller.sendMessage(msg);
892        }
893
894        public void setDesiredSize(int width, int height) {
895            Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height);
896            mCaller.sendMessage(msg);
897        }
898
899        public void setVisibility(boolean visible) {
900            Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
901                    visible ? 1 : 0);
902            mCaller.sendMessage(msg);
903        }
904
905        public void dispatchPointer(MotionEvent event) {
906            if (mEngine != null) {
907                mEngine.dispatchPointer(event);
908            }
909        }
910
911        public void dispatchWallpaperCommand(String action, int x, int y,
912                int z, Bundle extras) {
913            if (mEngine != null) {
914                mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false);
915            }
916        }
917
918        public void destroy() {
919            Message msg = mCaller.obtainMessage(DO_DETACH);
920            mCaller.sendMessage(msg);
921        }
922
923        public void executeMessage(Message message) {
924            switch (message.what) {
925                case DO_ATTACH: {
926                    try {
927                        mConnection.attachEngine(this);
928                    } catch (RemoteException e) {
929                        Log.w(TAG, "Wallpaper host disappeared", e);
930                        return;
931                    }
932                    Engine engine = onCreateEngine();
933                    mEngine = engine;
934                    mActiveEngines.add(engine);
935                    engine.attach(this);
936                    return;
937                }
938                case DO_DETACH: {
939                    mActiveEngines.remove(mEngine);
940                    mEngine.detach();
941                    return;
942                }
943                case DO_SET_DESIRED_SIZE: {
944                    mEngine.doDesiredSizeChanged(message.arg1, message.arg2);
945                    return;
946                }
947                case MSG_UPDATE_SURFACE:
948                    mEngine.updateSurface(true, false, false);
949                    break;
950                case MSG_VISIBILITY_CHANGED:
951                    if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
952                            + ": " + message.arg1);
953                    mEngine.doVisibilityChanged(message.arg1 != 0);
954                    break;
955                case MSG_WALLPAPER_OFFSETS: {
956                    mEngine.doOffsetsChanged();
957                } break;
958                case MSG_WALLPAPER_COMMAND: {
959                    WallpaperCommand cmd = (WallpaperCommand)message.obj;
960                    mEngine.doCommand(cmd);
961                } break;
962                case MSG_WINDOW_RESIZED: {
963                    final boolean reportDraw = message.arg1 != 0;
964                    mEngine.updateSurface(true, false, reportDraw);
965                    mEngine.doOffsetsChanged();
966                } break;
967                case MSG_TOUCH_EVENT: {
968                    boolean skip = false;
969                    MotionEvent ev = (MotionEvent)message.obj;
970                    if (ev.getAction() == MotionEvent.ACTION_MOVE) {
971                        synchronized (mEngine.mLock) {
972                            if (mEngine.mPendingMove == ev) {
973                                mEngine.mPendingMove = null;
974                            } else {
975                                // this is not the motion event we are looking for....
976                                skip = true;
977                            }
978                        }
979                    }
980                    if (!skip) {
981                        if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev);
982                        mEngine.onTouchEvent(ev);
983                    }
984                    ev.recycle();
985                } break;
986                default :
987                    Log.w(TAG, "Unknown message type " + message.what);
988            }
989        }
990    }
991
992    /**
993     * Implements the internal {@link IWallpaperService} interface to convert
994     * incoming calls to it back to calls on an {@link WallpaperService}.
995     */
996    class IWallpaperServiceWrapper extends IWallpaperService.Stub {
997        private final WallpaperService mTarget;
998
999        public IWallpaperServiceWrapper(WallpaperService context) {
1000            mTarget = context;
1001        }
1002
1003        public void attach(IWallpaperConnection conn, IBinder windowToken,
1004                int windowType, boolean isPreview, int reqWidth, int reqHeight) {
1005            new IWallpaperEngineWrapper(mTarget, conn, windowToken,
1006                    windowType, isPreview, reqWidth, reqHeight);
1007        }
1008    }
1009
1010    @Override
1011    public void onCreate() {
1012        super.onCreate();
1013    }
1014
1015    @Override
1016    public void onDestroy() {
1017        super.onDestroy();
1018        for (int i=0; i<mActiveEngines.size(); i++) {
1019            mActiveEngines.get(i).detach();
1020        }
1021        mActiveEngines.clear();
1022    }
1023
1024    /**
1025     * Implement to return the implementation of the internal accessibility
1026     * service interface.  Subclasses should not override.
1027     */
1028    @Override
1029    public final IBinder onBind(Intent intent) {
1030        return new IWallpaperServiceWrapper(this);
1031    }
1032
1033    /**
1034     * This allows subclasses to change the thread that most callbacks
1035     * occur on.  Currently hidden because it is mostly needed for the
1036     * image wallpaper (which runs in the system process and doesn't want
1037     * to get stuck running on that seriously in use main thread).  Not
1038     * exposed right now because the semantics of this are not totally
1039     * well defined and some callbacks can still happen on the main thread).
1040     * @hide
1041     */
1042    public void setCallbackLooper(Looper looper) {
1043        mCallbackLooper = looper;
1044    }
1045
1046    /**
1047     * Must be implemented to return a new instance of the wallpaper's engine.
1048     * Note that multiple instances may be active at the same time, such as
1049     * when the wallpaper is currently set as the active wallpaper and the user
1050     * is in the wallpaper picker viewing a preview of it as well.
1051     */
1052    public abstract Engine onCreateEngine();
1053
1054    @Override
1055    protected void dump(FileDescriptor fd, PrintWriter out, String[] args) {
1056        out.print("State of wallpaper "); out.print(this); out.println(":");
1057        for (int i=0; i<mActiveEngines.size(); i++) {
1058            Engine engine = mActiveEngines.get(i);
1059            out.print("  Engine "); out.print(engine); out.println(":");
1060            engine.dump("    ", fd, out, args);
1061        }
1062    }
1063}
1064