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