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